From 08729f1cdea36cc067b8881a4a5affe6711a221f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Benjamin=20H=C3=B6glinger?= Date: Thu, 10 May 2018 20:42:38 +0200 Subject: [PATCH] Implemented vigem_target_x360_get_user_index in library and driver --- .gitignore | 1 + include/ViGEmBusDriver.h | 3 ++- include/ViGEmBusShared.h | 33 +++++++++++++++++++++++++++++ include/ViGEmClient.h | 7 ++++--- lib/ViGEmClient.cpp | 45 ++++++++++++++++++++++++++++++++++++++-- sys/Driver.c | 2 +- sys/Queue.c | 40 +++++++++++++++++++++++++++++++++++ sys/Xusb.h | 1 + sys/buspdo.c | 16 ++++++++++++++ sys/usbpdo.c | 10 +++++++++ sys/xusb.c | 38 +++++++++++++++++++++++++++++++++ 11 files changed, 189 insertions(+), 7 deletions(-) diff --git a/.gitignore b/.gitignore index 3a15333..a3c5ce9 100644 --- a/.gitignore +++ b/.gitignore @@ -22,3 +22,4 @@ /sys/RCb21300 /sys/RCa21300 *.user +/.vs/config diff --git a/include/ViGEmBusDriver.h b/include/ViGEmBusDriver.h index 039ab0b..2a7c5c0 100644 --- a/include/ViGEmBusDriver.h +++ b/include/ViGEmBusDriver.h @@ -42,7 +42,8 @@ typedef enum _VIGEM_PDO_STAGE { ViGEmPdoCreate, ViGEmPdoPrepareHardware, - ViGEmPdoInternalIoControl + ViGEmPdoInternalIoControl, + ViGEmPdoInitFinished } VIGEM_PDO_STAGE, *PVIGEM_PDO_STAGE; diff --git a/include/ViGEmBusShared.h b/include/ViGEmBusShared.h index ff3b082..941da48 100644 --- a/include/ViGEmBusShared.h +++ b/include/ViGEmBusShared.h @@ -265,6 +265,39 @@ VOID FORCEINLINE XUSB_SUBMIT_REPORT_INIT( Report->SerialNo = SerialNo; } +typedef struct _XUSB_GET_USER_INDEX +{ + // + // sizeof(struct _XUSB_GET_USER_INDEX) + // + ULONG Size; + + // + // Serial number of target device. + // + ULONG SerialNo; + + // + // User index of target device. + // + OUT ULONG UserIndex; + +} XUSB_GET_USER_INDEX, *PXUSB_GET_USER_INDEX; + +// +// Initializes XUSB_GET_USER_INDEX structure. +// +VOID FORCEINLINE XUSB_GET_USER_INDEX_INIT( + _Out_ PXUSB_GET_USER_INDEX GetRequest, + _In_ ULONG SerialNo +) +{ + RtlZeroMemory(GetRequest, sizeof(XUSB_GET_USER_INDEX)); + + GetRequest->Size = sizeof(XUSB_GET_USER_INDEX); + GetRequest->SerialNo = SerialNo; +} + #pragma endregion #pragma region DualShock 4 section diff --git a/include/ViGEmClient.h b/include/ViGEmClient.h index 4df9a73..22ef1e4 100644 --- a/include/ViGEmClient.h +++ b/include/ViGEmClient.h @@ -451,7 +451,7 @@ VIGEM_API VIGEM_TARGET_TYPE vigem_target_get_type(PVIGEM_TARGET target); VIGEM_API BOOL vigem_target_is_attached(PVIGEM_TARGET target); /** - * \fn VIGEM_API ULONG vigem_target_x360_get_user_index(PVIGEM_CLIENT vigem, PVIGEM_TARGET target); + * \fn VIGEM_API VIGEM_ERROR vigem_target_x360_get_user_index(PVIGEM_CLIENT vigem, PVIGEM_TARGET target, PULONG index); * * \brief Returns the user index of the emulated Xenon device. This value correspondents to the * (zero-based) index number representing the player number via LED present on a @@ -462,10 +462,11 @@ VIGEM_API BOOL vigem_target_is_attached(PVIGEM_TARGET target); * * \param vigem The driver connection object. * \param target The target device object. + * \param index The (zero-based) user index of the Xenon device. * - * \return The user index of the Xenon device. + * \return A VIGEM_ERROR. */ -VIGEM_API ULONG vigem_target_x360_get_user_index(PVIGEM_CLIENT vigem, PVIGEM_TARGET target); +VIGEM_API VIGEM_ERROR vigem_target_x360_get_user_index(PVIGEM_CLIENT vigem, PVIGEM_TARGET target, PULONG index); #ifdef __cplusplus } diff --git a/lib/ViGEmClient.cpp b/lib/ViGEmClient.cpp index 8a7a236..4800980 100644 --- a/lib/ViGEmClient.cpp +++ b/lib/ViGEmClient.cpp @@ -635,7 +635,48 @@ BOOL vigem_target_is_attached(PVIGEM_TARGET target) return (target->State == VIGEM_TARGET_CONNECTED); } -ULONG vigem_target_x360_get_user_index(PVIGEM_CLIENT vigem, PVIGEM_TARGET target) +VIGEM_ERROR vigem_target_x360_get_user_index(PVIGEM_CLIENT vigem, PVIGEM_TARGET target, PULONG index) { - return VIGEM_API ULONG(); + if (vigem->hBusDevice == nullptr) + { + return VIGEM_ERROR_BUS_NOT_FOUND; + } + + if (target->SerialNo == 0 || target->Type != Xbox360Wired) + { + return VIGEM_ERROR_INVALID_TARGET; + } + + DWORD transfered = 0; + OVERLAPPED lOverlapped = { 0 }; + lOverlapped.hEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr); + + XUSB_GET_USER_INDEX gui; + XUSB_GET_USER_INDEX_INIT(&gui, target->SerialNo); + + DeviceIoControl( + vigem->hBusDevice, + IOCTL_XUSB_GET_USER_INDEX, + &gui, + gui.Size, + &gui, + gui.Size, + &transfered, + &lOverlapped + ); + + if (GetOverlappedResult(vigem->hBusDevice, &lOverlapped, &transfered, TRUE) == 0) + { + if (GetLastError() == ERROR_ACCESS_DENIED) + { + CloseHandle(lOverlapped.hEvent); + return VIGEM_ERROR_INVALID_TARGET; + } + } + + CloseHandle(lOverlapped.hEvent); + + *index = gui.UserIndex; + + return VIGEM_ERROR_NONE; } diff --git a/sys/Driver.c b/sys/Driver.c index 493522c..7a4ab22 100644 --- a/sys/Driver.c +++ b/sys/Driver.c @@ -411,7 +411,7 @@ Bus_PdoStageResult( // // If any stage fails or is last stage, get associated request and complete it // - if (!NT_SUCCESS(Status) || Stage == ViGEmPdoInternalIoControl) + if (!NT_SUCCESS(Status) || Stage == ViGEmPdoInitFinished) { WdfSpinLockAcquire(pFdoData->PendingPluginRequestsLock); diff --git a/sys/Queue.c b/sys/Queue.c index 08f211e..d8a3da7 100644 --- a/sys/Queue.c +++ b/sys/Queue.c @@ -50,6 +50,7 @@ VOID Bus_EvtIoDeviceControl( PXGIP_SUBMIT_REPORT xgipSubmit = NULL; PXGIP_SUBMIT_INTERRUPT xgipInterrupt = NULL; PVIGEM_CHECK_VERSION pCheckVersion = NULL; + PXUSB_GET_USER_INDEX pXusbGetUserIndex = NULL; Device = WdfIoQueueGetDevice(Queue); @@ -277,6 +278,45 @@ VOID Bus_EvtIoDeviceControl( break; #pragma endregion +#pragma region IOCTL_XUSB_GET_USER_INDEX + case IOCTL_XUSB_GET_USER_INDEX: + + KdPrint((DRIVERNAME "IOCTL_XUSB_GET_USER_INDEX")); + + // Don't accept the request if the output buffer can't hold the results + if (OutputBufferLength < sizeof(XUSB_GET_USER_INDEX)) + { + KdPrint((DRIVERNAME "IOCTL_XUSB_GET_USER_INDEX: output buffer too small: %ul\n", OutputBufferLength)); + break; + } + + status = WdfRequestRetrieveInputBuffer( + Request, + sizeof(XUSB_GET_USER_INDEX), + (PVOID)&pXusbGetUserIndex, + &length); + + if (!NT_SUCCESS(status)) + { + KdPrint((DRIVERNAME "WdfRequestRetrieveInputBuffer failed 0x%x\n", status)); + break; + } + + if ((sizeof(XUSB_GET_USER_INDEX) == pXusbGetUserIndex->Size) && (length == InputBufferLength)) + { + // This request only supports a single PDO at a time + if (pXusbGetUserIndex->SerialNo == 0) + { + status = STATUS_INVALID_PARAMETER; + break; + } + + status = Xusb_GetUserIndex(Device, pXusbGetUserIndex); + } + + break; +#pragma endregion + default: KdPrint((DRIVERNAME "UNKNOWN IOCTL CODE 0x%x\n", IoControlCode)); break; // default status is STATUS_INVALID_PARAMETER diff --git a/sys/Xusb.h b/sys/Xusb.h index 384903c..62be17e 100644 --- a/sys/Xusb.h +++ b/sys/Xusb.h @@ -124,3 +124,4 @@ NTSTATUS Xusb_AssignPdoContext(WDFDEVICE Device, PPDO_IDENTIFICATION_DESCRIPTION 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); diff --git a/sys/buspdo.c b/sys/buspdo.c index 108245d..debd191 100644 --- a/sys/buspdo.c +++ b/sys/buspdo.c @@ -700,6 +700,22 @@ VOID Pdo_EvtIoInternalDeviceControl( // success to the parent bus. // BUS_PDO_REPORT_STAGE_RESULT(pdoData->BusInterface, ViGEmPdoInternalIoControl, pdoData->SerialNo, status); + + // + // 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; diff --git a/sys/usbpdo.c b/sys/usbpdo.c index 7311007..a3bb46e 100644 --- a/sys/usbpdo.c +++ b/sys/usbpdo.c @@ -604,6 +604,16 @@ NTSTATUS UsbPdo_BulkOrInterruptTransfer(PURB urb, WDFDEVICE Device, WDFREQUEST R if (Buffer[2] == 0x05) xusb->LedNumber = 3; KdPrint((DRIVERNAME "-- LED Number: %d\n", xusb->LedNumber)); + + // + // Report back to FDO that we are ready to operate + // + BUS_PDO_REPORT_STAGE_RESULT( + pdoData->BusInterface, + ViGEmPdoInitFinished, + pdoData->SerialNo, + STATUS_SUCCESS + ); } } diff --git a/sys/xusb.c b/sys/xusb.c index 7bda9e0..a720b03 100644 --- a/sys/xusb.c +++ b/sys/xusb.c @@ -504,3 +504,41 @@ VOID Xusb_SelectConfiguration(PUSBD_INTERFACE_INFORMATION pInfo) pInfo->InterfaceHandle = (USBD_INTERFACE_HANDLE)0xFFFF0000; } +NTSTATUS Xusb_GetUserIndex(WDFDEVICE Device, PXUSB_GET_USER_INDEX Request) +{ + NTSTATUS status = STATUS_INVALID_PARAMETER; + WDFDEVICE hChild; + PPDO_DEVICE_DATA pdoData; + PXUSB_DEVICE_DATA xusbData; + + + KdPrint((DRIVERNAME "Entered Bus_QueueNotification\n")); + + hChild = Bus_GetPdo(Device, Request->SerialNo); + + // Validate child + if (hChild == NULL) + { + KdPrint((DRIVERNAME "Bus_QueueNotification: PDO with serial %d not found\n", Request->SerialNo)); + return STATUS_NO_SUCH_DEVICE; + } + + // Check common context + pdoData = PdoGetData(hChild); + if (pdoData == NULL) + { + KdPrint((DRIVERNAME "Bus_QueueNotification: PDO context not found\n")); + return STATUS_INVALID_PARAMETER; + } + + // Check if caller owns this PDO + if (!IS_OWNER(pdoData)) + { + KdPrint((DRIVERNAME "Bus_QueueNotification: PID mismatch: %d != %d\n", pdoData->OwnerProcessId, CURRENT_PROCESS_ID())); + return STATUS_ACCESS_DENIED; + } + + Request->UserIndex = (ULONG)XusbGetData(hChild)->LedNumber; + + return status; +}