diff --git a/sys/Ds4Pdo.cpp b/sys/Ds4Pdo.cpp index e18f0b5..5dd83f5 100644 --- a/sys/Ds4Pdo.cpp +++ b/sys/Ds4Pdo.cpp @@ -972,9 +972,10 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::UsbGetStringDescriptorType(PUR return STATUS_SUCCESS; } -NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::UsbBulkOrInterruptTransfer(_URB_BULK_OR_INTERRUPT_TRANSFER* pTransfer) +NTSTATUS ViGEm::Bus::Targets::EmulationTargetDS4::UsbBulkOrInterruptTransfer(_URB_BULK_OR_INTERRUPT_TRANSFER* pTransfer, WDFREQUEST Request) { UNREFERENCED_PARAMETER(pTransfer); + UNREFERENCED_PARAMETER(Request); return NTSTATUS(); } diff --git a/sys/Ds4Pdo.hpp b/sys/Ds4Pdo.hpp index af8dd54..2effd28 100644 --- a/sys/Ds4Pdo.hpp +++ b/sys/Ds4Pdo.hpp @@ -43,7 +43,7 @@ namespace ViGEm::Bus::Targets NTSTATUS UsbGetDescriptorFromInterface(PURB Urb) override; NTSTATUS UsbSelectInterface(PURB Urb) override; NTSTATUS UsbGetStringDescriptorType(PURB Urb) override; - NTSTATUS UsbBulkOrInterruptTransfer(_URB_BULK_OR_INTERRUPT_TRANSFER* pTransfer) override; + NTSTATUS UsbBulkOrInterruptTransfer(_URB_BULK_OR_INTERRUPT_TRANSFER* pTransfer, WDFREQUEST Request) override; private: static PCWSTR _deviceDescription; diff --git a/sys/EmulationTargetPDO.cpp b/sys/EmulationTargetPDO.cpp index d4bd197..71127c7 100644 --- a/sys/EmulationTargetPDO.cpp +++ b/sys/EmulationTargetPDO.cpp @@ -538,7 +538,7 @@ VOID ViGEm::Bus::Core::EmulationTargetPDO::EvtIoInternalDeviceControl( TRACE_BUSPDO, ">> >> URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER"); - //status = UsbPdo_BulkOrInterruptTransfer(urb, hDevice, Request); + status = ctx->Target->UsbBulkOrInterruptTransfer(&urb->UrbBulkOrInterruptTransfer, Request); break; diff --git a/sys/EmulationTargetPDO.hpp b/sys/EmulationTargetPDO.hpp index 6f05e61..5f6c5f3 100644 --- a/sys/EmulationTargetPDO.hpp +++ b/sys/EmulationTargetPDO.hpp @@ -68,7 +68,7 @@ namespace ViGEm::Bus::Core virtual NTSTATUS UsbGetStringDescriptorType(PURB Urb) = 0; - virtual NTSTATUS UsbBulkOrInterruptTransfer(struct _URB_BULK_OR_INTERRUPT_TRANSFER* pTransfer) = 0; + virtual NTSTATUS UsbBulkOrInterruptTransfer(struct _URB_BULK_OR_INTERRUPT_TRANSFER* pTransfer, WDFREQUEST Request) = 0; protected: static const ULONG _maxHardwareIdLength = 0xFF; diff --git a/sys/XusbPdo.cpp b/sys/XusbPdo.cpp index a14a1ec..6088258 100644 --- a/sys/XusbPdo.cpp +++ b/sys/XusbPdo.cpp @@ -6,6 +6,9 @@ #include +#include "busenum.h" +#include "ViGEmBusDriver.h" + PCWSTR ViGEm::Bus::Targets::EmulationTargetXUSB::_deviceDescription = L"Virtual Xbox 360 Controller"; @@ -709,9 +712,209 @@ NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::UsbGetStringDescriptorType(PU return STATUS_NOT_IMPLEMENTED; } -NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::UsbBulkOrInterruptTransfer(_URB_BULK_OR_INTERRUPT_TRANSFER* pTransfer) +NTSTATUS ViGEm::Bus::Targets::EmulationTargetXUSB::UsbBulkOrInterruptTransfer(_URB_BULK_OR_INTERRUPT_TRANSFER* pTransfer, WDFREQUEST Request) { - UNREFERENCED_PARAMETER(pTransfer); - - return NTSTATUS(); + NTSTATUS status; + WDFREQUEST notifyRequest; + + // Data coming FROM us TO higher driver + if (pTransfer->TransferFlags & USBD_TRANSFER_DIRECTION_IN) + { + TraceEvents(TRACE_LEVEL_VERBOSE, + TRACE_USBPDO, + ">> >> >> Incoming request, queuing..."); + + auto blobBuffer = static_cast(WdfMemoryGetBuffer(this->InterruptBlobStorage, nullptr)); + + if (xusb_is_data_pipe(pTransfer)) + { + // + // Send "boot sequence" first, then the actual inputs + // + switch (this->InterruptInitStage) + { + case 0: + pTransfer->TransferBufferLength = XUSB_INIT_STAGE_SIZE; + this->InterruptInitStage++; + RtlCopyMemory( + pTransfer->TransferBuffer, + &blobBuffer[XUSB_BLOB_00_OFFSET], + XUSB_INIT_STAGE_SIZE + ); + return STATUS_SUCCESS; + case 1: + pTransfer->TransferBufferLength = XUSB_INIT_STAGE_SIZE; + this->InterruptInitStage++; + RtlCopyMemory( + pTransfer->TransferBuffer, + &blobBuffer[XUSB_BLOB_01_OFFSET], + XUSB_INIT_STAGE_SIZE + ); + return STATUS_SUCCESS; + case 2: + pTransfer->TransferBufferLength = XUSB_INIT_STAGE_SIZE; + this->InterruptInitStage++; + RtlCopyMemory( + pTransfer->TransferBuffer, + &blobBuffer[XUSB_BLOB_02_OFFSET], + XUSB_INIT_STAGE_SIZE + ); + return STATUS_SUCCESS; + case 3: + pTransfer->TransferBufferLength = XUSB_INIT_STAGE_SIZE; + this->InterruptInitStage++; + RtlCopyMemory( + pTransfer->TransferBuffer, + &blobBuffer[XUSB_BLOB_03_OFFSET], + XUSB_INIT_STAGE_SIZE + ); + return STATUS_SUCCESS; + case 4: + pTransfer->TransferBufferLength = sizeof(XUSB_INTERRUPT_IN_PACKET); + this->InterruptInitStage++; + RtlCopyMemory( + pTransfer->TransferBuffer, + &blobBuffer[XUSB_BLOB_04_OFFSET], + sizeof(XUSB_INTERRUPT_IN_PACKET) + ); + return STATUS_SUCCESS; + case 5: + pTransfer->TransferBufferLength = XUSB_INIT_STAGE_SIZE; + this->InterruptInitStage++; + RtlCopyMemory( + pTransfer->TransferBuffer, + &blobBuffer[XUSB_BLOB_05_OFFSET], + XUSB_INIT_STAGE_SIZE + ); + return STATUS_SUCCESS; + default: + /* 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; + } + } + + if (xusb_is_control_pipe(pTransfer)) + { + if (!this->ReportedCapabilities && pTransfer->TransferBufferLength >= XUSB_INIT_STAGE_SIZE) + { + RtlCopyMemory( + pTransfer->TransferBuffer, + &blobBuffer[XUSB_BLOB_06_OFFSET], + XUSB_INIT_STAGE_SIZE + ); + + this->ReportedCapabilities = TRUE; + + return STATUS_SUCCESS; + } + + status = WdfRequestForwardToIoQueue(Request, this->HoldingUsbInRequests); + + return (NT_SUCCESS(status)) ? STATUS_PENDING : status; + } + } + + // Data coming FROM the higher driver TO us + TraceEvents(TRACE_LEVEL_VERBOSE, + TRACE_USBPDO, + ">> >> >> URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER: Handle %p, Flags %X, Length %d", + pTransfer->PipeHandle, + pTransfer->TransferFlags, + pTransfer->TransferBufferLength); + + if (pTransfer->TransferBufferLength == XUSB_LEDSET_SIZE) // Led + { + auto Buffer = static_cast(pTransfer->TransferBuffer); + + TraceEvents(TRACE_LEVEL_VERBOSE, + TRACE_USBPDO, + "-- LED Buffer: %02X %02X %02X", + Buffer[0], Buffer[1], Buffer[2]); + + // extract LED byte to get controller slot + if (Buffer[0] == 0x01 && Buffer[1] == 0x03 && Buffer[2] >= 0x02) + { + if (Buffer[2] == 0x02)this->LedNumber = 0; + if (Buffer[2] == 0x03)this->LedNumber = 1; + if (Buffer[2] == 0x04)this->LedNumber = 2; + if (Buffer[2] == 0x05)this->LedNumber = 3; + + TraceEvents(TRACE_LEVEL_INFORMATION, + TRACE_USBPDO, + "-- LED Number: %d", + this->LedNumber); + + // + // Notify client library that PDO is ready + // + KeSetEvent(&this->PdoBootNotificationEvent, 0, FALSE); + } + } + + // Extract rumble (vibration) information + if (pTransfer->TransferBufferLength == XUSB_RUMBLE_SIZE) + { + auto Buffer = static_cast(pTransfer->TransferBuffer); + + TraceEvents(TRACE_LEVEL_VERBOSE, + TRACE_USBPDO, + "-- Rumble Buffer: %02X %02X %02X %02X %02X %02X %02X %02X", + Buffer[0], + Buffer[1], + Buffer[2], + Buffer[3], + Buffer[4], + Buffer[5], + Buffer[6], + Buffer[7]); + + RtlCopyBytes(this->Rumble, Buffer, pTransfer->TransferBufferLength); + } + + // Notify user-mode process that new data is available + status = WdfIoQueueRetrieveNextRequest(this->PendingNotificationRequests, ¬ifyRequest); + + if (NT_SUCCESS(status)) + { + PXUSB_REQUEST_NOTIFICATION notify = NULL; + + status = WdfRequestRetrieveOutputBuffer( + notifyRequest, + sizeof(XUSB_REQUEST_NOTIFICATION), + reinterpret_cast(¬ify), + nullptr + ); + + if (NT_SUCCESS(status)) + { + // Assign values to output buffer + notify->Size = sizeof(XUSB_REQUEST_NOTIFICATION); + notify->SerialNo = this->SerialNo; + notify->LedNumber = this->LedNumber; + notify->LargeMotor = this->Rumble[3]; + notify->SmallMotor = this->Rumble[4]; + + WdfRequestCompleteWithInformation(notifyRequest, status, notify->Size); + } + else + { + TraceEvents(TRACE_LEVEL_ERROR, + TRACE_USBPDO, + "WdfRequestRetrieveOutputBuffer failed with status %!STATUS!", + status); + } + } + else + { + TraceEvents(TRACE_LEVEL_WARNING, + TRACE_USBPDO, + "!! [XUSB] WdfIoQueueRetrieveNextRequest failed with status %!STATUS!", + status); + } + + return status; } diff --git a/sys/XusbPdo.hpp b/sys/XusbPdo.hpp index e91ab40..1ee5fc3 100644 --- a/sys/XusbPdo.hpp +++ b/sys/XusbPdo.hpp @@ -15,7 +15,16 @@ namespace ViGEm::Bus::Targets XUSB_REPORT Report; } XUSB_INTERRUPT_IN_PACKET, *PXUSB_INTERRUPT_IN_PACKET; + constexpr bool xusb_is_data_pipe(_URB_BULK_OR_INTERRUPT_TRANSFER* pTransfer) + { + return (pTransfer->PipeHandle == reinterpret_cast(0xFFFF0081)); + } + constexpr bool xusb_is_control_pipe(_URB_BULK_OR_INTERRUPT_TRANSFER* pTransfer) + { + return (pTransfer->PipeHandle == reinterpret_cast(0xFFFF0083)); + } + class EmulationTargetXUSB : public Core::EmulationTargetPDO { public: @@ -42,7 +51,7 @@ namespace ViGEm::Bus::Targets NTSTATUS UsbGetDescriptorFromInterface(PURB Urb) override; NTSTATUS UsbSelectInterface(PURB Urb) override; NTSTATUS UsbGetStringDescriptorType(PURB Urb) override; - NTSTATUS UsbBulkOrInterruptTransfer(_URB_BULK_OR_INTERRUPT_TRANSFER* pTransfer) override; + NTSTATUS UsbBulkOrInterruptTransfer(_URB_BULK_OR_INTERRUPT_TRANSFER* pTransfer, WDFREQUEST Request) override; private: static PCWSTR _deviceDescription;