mirror of
https://github.com/nefarius/ViGEmBus.git
synced 2025-08-10 00:52:17 +00:00
1290 lines
48 KiB
C
1290 lines
48 KiB
C
/*
|
|
MIT License
|
|
|
|
Copyright (c) 2016 Benjamin "Nefarius" Höglinger
|
|
|
|
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 "usbpdo.tmh"
|
|
|
|
|
|
//
|
|
// Dummy function to satisfy USB interface
|
|
//
|
|
BOOLEAN USB_BUSIFFN UsbPdo_IsDeviceHighSpeed(IN PVOID BusContext)
|
|
{
|
|
UNREFERENCED_PARAMETER(BusContext);
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION,
|
|
TRACE_USBPDO,
|
|
"IsDeviceHighSpeed: TRUE");
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//
|
|
// Dummy function to satisfy USB interface
|
|
//
|
|
NTSTATUS USB_BUSIFFN UsbPdo_QueryBusInformation(
|
|
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);
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION,
|
|
TRACE_USBPDO,
|
|
"QueryBusInformation: %!STATUS!", STATUS_UNSUCCESSFUL);
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Dummy function to satisfy USB interface
|
|
//
|
|
NTSTATUS USB_BUSIFFN UsbPdo_SubmitIsoOutUrb(IN PVOID BusContext, IN PURB Urb)
|
|
{
|
|
UNREFERENCED_PARAMETER(BusContext);
|
|
UNREFERENCED_PARAMETER(Urb);
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION,
|
|
TRACE_USBPDO,
|
|
"SubmitIsoOutUrb: %!STATUS!", STATUS_UNSUCCESSFUL);
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Dummy function to satisfy USB interface
|
|
//
|
|
NTSTATUS USB_BUSIFFN UsbPdo_QueryBusTime(IN PVOID BusContext, IN OUT PULONG CurrentUsbFrame)
|
|
{
|
|
UNREFERENCED_PARAMETER(BusContext);
|
|
UNREFERENCED_PARAMETER(CurrentUsbFrame);
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION,
|
|
TRACE_USBPDO,
|
|
"QueryBusTime: %!STATUS!", STATUS_UNSUCCESSFUL);
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
//
|
|
// Dummy function to satisfy USB interface
|
|
//
|
|
VOID USB_BUSIFFN UsbPdo_GetUSBDIVersion(
|
|
IN PVOID BusContext,
|
|
IN OUT PUSBD_VERSION_INFORMATION VersionInformation,
|
|
IN OUT PULONG HcdCapabilities
|
|
)
|
|
{
|
|
UNREFERENCED_PARAMETER(BusContext);
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION,
|
|
TRACE_USBPDO,
|
|
"GetUSBDIVersion: 0x500, 0x200");
|
|
|
|
if (VersionInformation != NULL)
|
|
{
|
|
VersionInformation->USBDI_Version = 0x500; /* Usbport */
|
|
VersionInformation->Supported_USB_Version = 0x200; /* USB 2.0 */
|
|
}
|
|
|
|
if (HcdCapabilities != NULL)
|
|
{
|
|
*HcdCapabilities = 0;
|
|
}
|
|
}
|
|
|
|
//
|
|
// Set device descriptor to identify the current USB device.
|
|
//
|
|
NTSTATUS UsbPdo_GetDeviceDescriptorType(PURB urb, PPDO_DEVICE_DATA pCommon)
|
|
{
|
|
PUSB_DEVICE_DESCRIPTOR pDescriptor = (PUSB_DEVICE_DESCRIPTOR)urb->UrbControlDescriptorRequest.TransferBuffer;
|
|
|
|
switch (pCommon->TargetType)
|
|
{
|
|
case Xbox360Wired:
|
|
|
|
Xusb_GetDeviceDescriptorType(pDescriptor, pCommon);
|
|
|
|
break;
|
|
|
|
case DualShock4Wired:
|
|
|
|
Ds4_GetDeviceDescriptorType(pDescriptor, pCommon);
|
|
|
|
break;
|
|
|
|
case XboxOneWired:
|
|
|
|
Xgip_GetDeviceDescriptorType(pDescriptor, pCommon);
|
|
|
|
break;
|
|
|
|
default:
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Set configuration descriptor, expose interfaces and endpoints.
|
|
//
|
|
NTSTATUS UsbPdo_GetConfigurationDescriptorType(PURB urb, PPDO_DEVICE_DATA pCommon)
|
|
{
|
|
PUCHAR Buffer = (PUCHAR)urb->UrbControlDescriptorRequest.TransferBuffer;
|
|
|
|
// First request just gets required buffer size back
|
|
if (urb->UrbControlDescriptorRequest.TransferBufferLength == sizeof(USB_CONFIGURATION_DESCRIPTOR))
|
|
{
|
|
ULONG length = sizeof(USB_CONFIGURATION_DESCRIPTOR);
|
|
|
|
switch (pCommon->TargetType)
|
|
{
|
|
case Xbox360Wired:
|
|
|
|
Xusb_GetConfigurationDescriptorType(Buffer, length);
|
|
|
|
break;
|
|
case DualShock4Wired:
|
|
|
|
Ds4_GetConfigurationDescriptorType(Buffer, length);
|
|
|
|
break;
|
|
case XboxOneWired:
|
|
|
|
Xgip_GetConfigurationDescriptorType(Buffer, length);
|
|
|
|
break;
|
|
default:
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
}
|
|
|
|
ULONG length = urb->UrbControlDescriptorRequest.TransferBufferLength;
|
|
|
|
// Second request can store the whole descriptor
|
|
switch (pCommon->TargetType)
|
|
{
|
|
case Xbox360Wired:
|
|
|
|
if (length >= XUSB_DESCRIPTOR_SIZE)
|
|
{
|
|
Xusb_GetConfigurationDescriptorType(Buffer, XUSB_DESCRIPTOR_SIZE);
|
|
}
|
|
|
|
break;
|
|
case DualShock4Wired:
|
|
|
|
if (length >= DS4_DESCRIPTOR_SIZE)
|
|
{
|
|
Ds4_GetConfigurationDescriptorType(Buffer, DS4_DESCRIPTOR_SIZE);
|
|
}
|
|
|
|
break;
|
|
case XboxOneWired:
|
|
|
|
if (length >= XGIP_DESCRIPTOR_SIZE)
|
|
{
|
|
Xgip_GetConfigurationDescriptorType(Buffer, XGIP_DESCRIPTOR_SIZE);
|
|
}
|
|
|
|
break;
|
|
default:
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Set device string descriptors (currently only used in DS4 emulation).
|
|
//
|
|
NTSTATUS UsbPdo_GetStringDescriptorType(PURB urb, PPDO_DEVICE_DATA pCommon)
|
|
{
|
|
TraceEvents(TRACE_LEVEL_VERBOSE,
|
|
TRACE_USBPDO,
|
|
"Index = %d",
|
|
urb->UrbControlDescriptorRequest.Index);
|
|
|
|
switch (pCommon->TargetType)
|
|
{
|
|
case DualShock4Wired:
|
|
{
|
|
switch (urb->UrbControlDescriptorRequest.Index)
|
|
{
|
|
case 0:
|
|
{
|
|
// "American English"
|
|
UCHAR LangId[HID_LANGUAGE_ID_LENGTH] =
|
|
{
|
|
0x04, 0x03, 0x09, 0x04
|
|
};
|
|
|
|
urb->UrbControlDescriptorRequest.TransferBufferLength = HID_LANGUAGE_ID_LENGTH;
|
|
RtlCopyBytes(urb->UrbControlDescriptorRequest.TransferBuffer, LangId, HID_LANGUAGE_ID_LENGTH);
|
|
|
|
break;
|
|
}
|
|
case 1:
|
|
{
|
|
TraceEvents(TRACE_LEVEL_VERBOSE,
|
|
TRACE_USBPDO,
|
|
"LanguageId = 0x%X",
|
|
urb->UrbControlDescriptorRequest.LanguageId);
|
|
|
|
if (urb->UrbControlDescriptorRequest.TransferBufferLength < DS4_MANUFACTURER_NAME_LENGTH)
|
|
{
|
|
PUSB_STRING_DESCRIPTOR pDesc = (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:
|
|
{
|
|
TraceEvents(TRACE_LEVEL_VERBOSE,
|
|
TRACE_USBPDO,
|
|
"LanguageId = 0x%X",
|
|
urb->UrbControlDescriptorRequest.LanguageId);
|
|
|
|
if (urb->UrbControlDescriptorRequest.TransferBufferLength < DS4_PRODUCT_NAME_LENGTH)
|
|
{
|
|
PUSB_STRING_DESCRIPTOR pDesc = (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;
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Fakes a successfully selected configuration.
|
|
//
|
|
NTSTATUS UsbPdo_SelectConfiguration(PURB urb, PPDO_DEVICE_DATA pCommon)
|
|
{
|
|
PUSBD_INTERFACE_INFORMATION pInfo;
|
|
|
|
pInfo = &urb->UrbSelectConfiguration.Interface;
|
|
|
|
TraceEvents(TRACE_LEVEL_VERBOSE,
|
|
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");
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
switch (pCommon->TargetType)
|
|
{
|
|
case Xbox360Wired:
|
|
|
|
if (urb->UrbHeader.Length < XUSB_CONFIGURATION_SIZE)
|
|
{
|
|
TraceEvents(TRACE_LEVEL_WARNING,
|
|
TRACE_USBPDO,
|
|
">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: Invalid ConfigurationDescriptor");
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
Xusb_SelectConfiguration(pInfo);
|
|
|
|
break;
|
|
|
|
case DualShock4Wired:
|
|
|
|
if (urb->UrbHeader.Length < DS4_CONFIGURATION_SIZE)
|
|
{
|
|
TraceEvents(TRACE_LEVEL_WARNING,
|
|
TRACE_USBPDO,
|
|
">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: Invalid ConfigurationDescriptor");
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
Ds4_SelectConfiguration(pInfo);
|
|
|
|
break;
|
|
|
|
case XboxOneWired:
|
|
|
|
if (urb->UrbHeader.Length < XGIP_CONFIGURATION_SIZE)
|
|
{
|
|
TraceEvents(TRACE_LEVEL_WARNING,
|
|
TRACE_USBPDO,
|
|
">> >> >> URB_FUNCTION_SELECT_CONFIGURATION: Invalid ConfigurationDescriptor");
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
Xgip_SelectConfiguration(pInfo);
|
|
|
|
break;
|
|
|
|
default:
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Fakes a successfully selected interface.
|
|
//
|
|
NTSTATUS UsbPdo_SelectInterface(PURB urb, PPDO_DEVICE_DATA pCommon)
|
|
{
|
|
PUSBD_INTERFACE_INFORMATION pInfo = &urb->UrbSelectInterface.Interface;
|
|
|
|
TraceEvents(TRACE_LEVEL_VERBOSE,
|
|
TRACE_USBPDO,
|
|
">> >> >> URB_FUNCTION_SELECT_INTERFACE: Length %d, Interface %d, Alternate %d, Pipes %d",
|
|
(int)pInfo->Length,
|
|
(int)pInfo->InterfaceNumber,
|
|
(int)pInfo->AlternateSetting,
|
|
pInfo->NumberOfPipes);
|
|
|
|
TraceEvents(TRACE_LEVEL_VERBOSE,
|
|
TRACE_USBPDO,
|
|
">> >> >> URB_FUNCTION_SELECT_INTERFACE: Class %d, SubClass %d, Protocol %d",
|
|
(int)pInfo->Class,
|
|
(int)pInfo->SubClass,
|
|
(int)pInfo->Protocol);
|
|
|
|
switch (pCommon->TargetType)
|
|
{
|
|
case Xbox360Wired:
|
|
{
|
|
if (pInfo->InterfaceNumber == 1)
|
|
{
|
|
pInfo[0].Class = 0xFF;
|
|
pInfo[0].SubClass = 0x5D;
|
|
pInfo[0].Protocol = 0x03;
|
|
pInfo[0].NumberOfPipes = 0x04;
|
|
|
|
pInfo[0].InterfaceHandle = (USBD_INTERFACE_HANDLE)0xFFFF0000;
|
|
|
|
pInfo[0].Pipes[0].MaximumTransferSize = 0x00400000;
|
|
pInfo[0].Pipes[0].MaximumPacketSize = 0x20;
|
|
pInfo[0].Pipes[0].EndpointAddress = 0x82;
|
|
pInfo[0].Pipes[0].Interval = 0x04;
|
|
pInfo[0].Pipes[0].PipeType = 0x03;
|
|
pInfo[0].Pipes[0].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0082;
|
|
pInfo[0].Pipes[0].PipeFlags = 0x00;
|
|
|
|
pInfo[0].Pipes[1].MaximumTransferSize = 0x00400000;
|
|
pInfo[0].Pipes[1].MaximumPacketSize = 0x20;
|
|
pInfo[0].Pipes[1].EndpointAddress = 0x02;
|
|
pInfo[0].Pipes[1].Interval = 0x08;
|
|
pInfo[0].Pipes[1].PipeType = 0x03;
|
|
pInfo[0].Pipes[1].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0002;
|
|
pInfo[0].Pipes[1].PipeFlags = 0x00;
|
|
|
|
pInfo[0].Pipes[2].MaximumTransferSize = 0x00400000;
|
|
pInfo[0].Pipes[2].MaximumPacketSize = 0x20;
|
|
pInfo[0].Pipes[2].EndpointAddress = 0x83;
|
|
pInfo[0].Pipes[2].Interval = 0x08;
|
|
pInfo[0].Pipes[2].PipeType = 0x03;
|
|
pInfo[0].Pipes[2].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0083;
|
|
pInfo[0].Pipes[2].PipeFlags = 0x00;
|
|
|
|
pInfo[0].Pipes[3].MaximumTransferSize = 0x00400000;
|
|
pInfo[0].Pipes[3].MaximumPacketSize = 0x20;
|
|
pInfo[0].Pipes[3].EndpointAddress = 0x03;
|
|
pInfo[0].Pipes[3].Interval = 0x08;
|
|
pInfo[0].Pipes[3].PipeType = 0x03;
|
|
pInfo[0].Pipes[3].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0003;
|
|
pInfo[0].Pipes[3].PipeFlags = 0x00;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
if (pInfo->InterfaceNumber == 2)
|
|
{
|
|
pInfo[0].Class = 0xFF;
|
|
pInfo[0].SubClass = 0x5D;
|
|
pInfo[0].Protocol = 0x02;
|
|
pInfo[0].NumberOfPipes = 0x01;
|
|
|
|
pInfo[0].InterfaceHandle = (USBD_INTERFACE_HANDLE)0xFFFF0000;
|
|
|
|
pInfo[0].Pipes[0].MaximumTransferSize = 0x00400000;
|
|
pInfo[0].Pipes[0].MaximumPacketSize = 0x20;
|
|
pInfo[0].Pipes[0].EndpointAddress = 0x84;
|
|
pInfo[0].Pipes[0].Interval = 0x04;
|
|
pInfo[0].Pipes[0].PipeType = 0x03;
|
|
pInfo[0].Pipes[0].PipeHandle = (USBD_PIPE_HANDLE)0xFFFF0084;
|
|
pInfo[0].Pipes[0].PipeFlags = 0x00;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
}
|
|
case DualShock4Wired:
|
|
{
|
|
TraceEvents(TRACE_LEVEL_WARNING,
|
|
TRACE_USBPDO,
|
|
"Not implemented");
|
|
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
//
|
|
// Dispatch interrupt transfers.
|
|
//
|
|
NTSTATUS UsbPdo_BulkOrInterruptTransfer(PURB urb, WDFDEVICE Device, WDFREQUEST Request)
|
|
{
|
|
struct _URB_BULK_OR_INTERRUPT_TRANSFER* pTransfer = &urb->UrbBulkOrInterruptTransfer;
|
|
NTSTATUS status;
|
|
PPDO_DEVICE_DATA pdoData;
|
|
WDFREQUEST notifyRequest;
|
|
|
|
pdoData = PdoGetData(Device);
|
|
|
|
if (pdoData == NULL)
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR,
|
|
TRACE_USBPDO,
|
|
">> >> >> PdoGetData failed");
|
|
return STATUS_INVALID_PARAMETER;
|
|
}
|
|
|
|
switch (pdoData->TargetType)
|
|
{
|
|
case Xbox360Wired:
|
|
{
|
|
PXUSB_DEVICE_DATA xusb = XusbGetData(Device);
|
|
|
|
// Check context
|
|
if (xusb == NULL)
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR,
|
|
TRACE_USBPDO,
|
|
"No XUSB context found on device %p",
|
|
Device);
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
// Data coming FROM us TO higher driver
|
|
if (pTransfer->TransferFlags & USBD_TRANSFER_DIRECTION_IN)
|
|
{
|
|
TraceEvents(TRACE_LEVEL_VERBOSE,
|
|
TRACE_USBPDO,
|
|
">> >> >> Incoming request, queuing...");
|
|
|
|
if (XUSB_IS_DATA_PIPE(pTransfer))
|
|
{
|
|
//
|
|
// Send "boot sequence" first, then the actual inputs
|
|
//
|
|
switch (xusb->InterruptInitStage)
|
|
{
|
|
case 0:
|
|
xusb->InterruptInitStage++;
|
|
pTransfer->TransferBufferLength = XUSB_INIT_STAGE_SIZE;
|
|
COPY_BYTE_ARRAY(pTransfer->TransferBuffer, P99_PROTECT({
|
|
0x01, 0x03, 0x0E
|
|
}));
|
|
return STATUS_SUCCESS;
|
|
case 1:
|
|
xusb->InterruptInitStage++;
|
|
pTransfer->TransferBufferLength = XUSB_INIT_STAGE_SIZE;
|
|
COPY_BYTE_ARRAY(pTransfer->TransferBuffer, P99_PROTECT({
|
|
0x02, 0x03, 0x00
|
|
}));
|
|
return STATUS_SUCCESS;
|
|
case 2:
|
|
xusb->InterruptInitStage++;
|
|
pTransfer->TransferBufferLength = XUSB_INIT_STAGE_SIZE;
|
|
COPY_BYTE_ARRAY(pTransfer->TransferBuffer, P99_PROTECT({
|
|
0x03, 0x03, 0x03
|
|
}));
|
|
return STATUS_SUCCESS;
|
|
case 3:
|
|
xusb->InterruptInitStage++;
|
|
pTransfer->TransferBufferLength = XUSB_INIT_STAGE_SIZE;
|
|
COPY_BYTE_ARRAY(pTransfer->TransferBuffer, P99_PROTECT({
|
|
0x08, 0x03, 0x00
|
|
}));
|
|
return STATUS_SUCCESS;
|
|
case 4:
|
|
xusb->InterruptInitStage++;
|
|
pTransfer->TransferBufferLength = sizeof(XUSB_INTERRUPT_IN_PACKET);
|
|
COPY_BYTE_ARRAY(pTransfer->TransferBuffer, P99_PROTECT({
|
|
0x00, 0x14, 0x00, 0x00, 0x00, 0x00, 0xe4, 0xf2,
|
|
0xb3, 0xf8, 0x49, 0xf3, 0xb0, 0xfc, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00
|
|
}));
|
|
return STATUS_SUCCESS;
|
|
case 5:
|
|
xusb->InterruptInitStage++;
|
|
pTransfer->TransferBufferLength = XUSB_INIT_STAGE_SIZE;
|
|
COPY_BYTE_ARRAY(pTransfer->TransferBuffer, P99_PROTECT({
|
|
0x01, 0x03, 0x03
|
|
}));
|
|
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, pdoData->PendingUsbInRequests);
|
|
|
|
return (NT_SUCCESS(status)) ? STATUS_PENDING : status;
|
|
}
|
|
}
|
|
|
|
if (XUSB_IS_CONTROL_PIPE(pTransfer))
|
|
{
|
|
if (!xusb->ReportedCapabilities && pTransfer->TransferBufferLength >= XUSB_INIT_STAGE_SIZE)
|
|
{
|
|
pTransfer->TransferBufferLength = XUSB_INIT_STAGE_SIZE;
|
|
COPY_BYTE_ARRAY(pTransfer->TransferBuffer, P99_PROTECT({
|
|
0x05, 0x03, 0x00
|
|
}));
|
|
|
|
xusb->ReportedCapabilities = TRUE;
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
status = WdfRequestForwardToIoQueue(Request, xusb->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
|
|
{
|
|
PUCHAR Buffer = 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) xusb->LedNumber = 0;
|
|
if (Buffer[2] == 0x03) xusb->LedNumber = 1;
|
|
if (Buffer[2] == 0x04) xusb->LedNumber = 2;
|
|
if (Buffer[2] == 0x05) xusb->LedNumber = 3;
|
|
|
|
TraceEvents(TRACE_LEVEL_INFORMATION,
|
|
TRACE_USBPDO,
|
|
"-- LED Number: %d",
|
|
xusb->LedNumber);
|
|
}
|
|
}
|
|
|
|
// Extract rumble (vibration) information
|
|
if (pTransfer->TransferBufferLength == XUSB_RUMBLE_SIZE)
|
|
{
|
|
PUCHAR Buffer = 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(xusb->Rumble, Buffer, pTransfer->TransferBufferLength);
|
|
}
|
|
|
|
// Notify user-mode process that new data is available
|
|
status = WdfIoQueueRetrieveNextRequest(pdoData->PendingNotificationRequests, ¬ifyRequest);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
PXUSB_REQUEST_NOTIFICATION notify = NULL;
|
|
|
|
status = WdfRequestRetrieveOutputBuffer(notifyRequest, sizeof(XUSB_REQUEST_NOTIFICATION), (PVOID)¬ify, NULL);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
// Assign values to output buffer
|
|
notify->Size = sizeof(XUSB_REQUEST_NOTIFICATION);
|
|
notify->SerialNo = pdoData->SerialNo;
|
|
notify->LedNumber = xusb->LedNumber;
|
|
notify->LargeMotor = xusb->Rumble[3];
|
|
notify->SmallMotor = xusb->Rumble[4];
|
|
|
|
WdfRequestCompleteWithInformation(notifyRequest, status, notify->Size);
|
|
}
|
|
else
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR,
|
|
TRACE_USBPDO,
|
|
"WdfRequestRetrieveOutputBuffer failed with status %!STATUS!",
|
|
status);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case DualShock4Wired:
|
|
{
|
|
PDS4_DEVICE_DATA ds4Data = Ds4GetData(Device);
|
|
|
|
// Data coming FROM us TO higher driver
|
|
if (pTransfer->TransferFlags & USBD_TRANSFER_DIRECTION_IN
|
|
&& pTransfer->PipeHandle == (USBD_PIPE_HANDLE)0xFFFF0084)
|
|
{
|
|
TraceEvents(TRACE_LEVEL_VERBOSE,
|
|
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, pdoData->PendingUsbInRequests);
|
|
|
|
return (NT_SUCCESS(status)) ? STATUS_PENDING : status;
|
|
}
|
|
|
|
// Store relevant bytes of buffer in PDO context
|
|
RtlCopyBytes(&ds4Data->OutputReport,
|
|
(PUCHAR)pTransfer->TransferBuffer + DS4_OUTPUT_BUFFER_OFFSET,
|
|
DS4_OUTPUT_BUFFER_LENGTH);
|
|
|
|
// Notify user-mode process that new data is available
|
|
status = WdfIoQueueRetrieveNextRequest(pdoData->PendingNotificationRequests, ¬ifyRequest);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
PDS4_REQUEST_NOTIFICATION notify = NULL;
|
|
|
|
status = WdfRequestRetrieveOutputBuffer(notifyRequest, sizeof(DS4_REQUEST_NOTIFICATION), (PVOID)¬ify, NULL);
|
|
|
|
if (NT_SUCCESS(status))
|
|
{
|
|
// Assign values to output buffer
|
|
notify->Size = sizeof(DS4_REQUEST_NOTIFICATION);
|
|
notify->SerialNo = pdoData->SerialNo;
|
|
notify->Report = ds4Data->OutputReport;
|
|
|
|
WdfRequestCompleteWithInformation(notifyRequest, status, notify->Size);
|
|
}
|
|
else
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR,
|
|
TRACE_USBPDO,
|
|
"WdfRequestRetrieveOutputBuffer failed with status %!STATUS!",
|
|
status);
|
|
}
|
|
}
|
|
|
|
break;
|
|
}
|
|
case XboxOneWired:
|
|
{
|
|
PXGIP_DEVICE_DATA xgipData = XgipGetData(Device);
|
|
|
|
// Data coming FROM us TO higher driver
|
|
if (pTransfer->TransferFlags & USBD_TRANSFER_DIRECTION_IN)
|
|
{
|
|
KdPrint((DRIVERNAME ">> >> >> 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, xgipData->PendingUsbInRequests);
|
|
|
|
return (NT_SUCCESS(status)) ? STATUS_PENDING : status;
|
|
}
|
|
|
|
// Data coming FROM the higher driver TO us
|
|
KdPrint((DRIVERNAME ">> >> >> URB_FUNCTION_BULK_OR_INTERRUPT_TRANSFER: Handle %p, Flags %X, Length %d",
|
|
pTransfer->PipeHandle,
|
|
pTransfer->TransferFlags,
|
|
pTransfer->TransferBufferLength));
|
|
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Clean-up actions on shutdown.
|
|
//
|
|
NTSTATUS UsbPdo_AbortPipe(WDFDEVICE Device)
|
|
{
|
|
PPDO_DEVICE_DATA pdoData = PdoGetData(Device);
|
|
|
|
switch (pdoData->TargetType)
|
|
{
|
|
case DualShock4Wired:
|
|
{
|
|
PDS4_DEVICE_DATA ds4 = Ds4GetData(Device);
|
|
|
|
// Check context
|
|
if (ds4 == NULL)
|
|
{
|
|
TraceEvents(TRACE_LEVEL_ERROR,
|
|
TRACE_USBPDO,
|
|
"No DS4 context found on device %p", Device);
|
|
|
|
return STATUS_UNSUCCESSFUL;
|
|
}
|
|
|
|
// Higher driver shutting down, emptying PDOs queues
|
|
WdfTimerStop(ds4->PendingUsbInRequestsTimer, TRUE);
|
|
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
// Higher driver shutting down, emptying PDOs queues
|
|
WdfIoQueuePurge(pdoData->PendingUsbInRequests, NULL, NULL);
|
|
WdfIoQueuePurge(pdoData->PendingNotificationRequests, NULL, NULL);
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Processes URBs containing HID-related requests.
|
|
//
|
|
NTSTATUS UsbPdo_ClassInterface(PURB urb, WDFDEVICE Device, PPDO_DEVICE_DATA pCommon)
|
|
{
|
|
struct _URB_CONTROL_VENDOR_OR_CLASS_REQUEST* pRequest = &urb->UrbControlVendorClassRequest;
|
|
|
|
TraceEvents(TRACE_LEVEL_VERBOSE,
|
|
TRACE_USBPDO,
|
|
">> >> >> URB_FUNCTION_CLASS_INTERFACE");
|
|
TraceEvents(TRACE_LEVEL_VERBOSE,
|
|
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 (pCommon->TargetType)
|
|
{
|
|
case DualShock4Wired:
|
|
{
|
|
PDS4_DEVICE_DATA ds4 = Ds4GetData(Device);
|
|
|
|
switch (pRequest->Request)
|
|
{
|
|
case HID_REQUEST_GET_REPORT:
|
|
{
|
|
UCHAR reportId = HID_GET_REPORT_ID(pRequest);
|
|
UCHAR reportType = HID_GET_REPORT_TYPE(pRequest);
|
|
|
|
TraceEvents(TRACE_LEVEL_VERBOSE,
|
|
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[HID_GET_FEATURE_REPORT_SIZE_0] =
|
|
{
|
|
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 = HID_GET_FEATURE_REPORT_SIZE_0;
|
|
RtlCopyBytes(pRequest->TransferBuffer, Response, HID_GET_FEATURE_REPORT_SIZE_0);
|
|
|
|
break;
|
|
}
|
|
case HID_REPORT_ID_1:
|
|
{
|
|
// Source: http://eleccelerator.com/wiki/index.php?title=DualShock_4#Class_Requests
|
|
UCHAR Response[HID_GET_FEATURE_REPORT_SIZE_1] =
|
|
{
|
|
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 = HID_GET_FEATURE_REPORT_SIZE_1;
|
|
RtlCopyBytes(pRequest->TransferBuffer, Response, HID_GET_FEATURE_REPORT_SIZE_1);
|
|
|
|
break;
|
|
}
|
|
case HID_REPORT_MAC_ADDRESSES_ID:
|
|
{
|
|
// Source: http://eleccelerator.com/wiki/index.php?title=DualShock_4#Class_Requests
|
|
UCHAR Response[HID_GET_FEATURE_REPORT_MAC_ADDRESSES_SIZE] =
|
|
{
|
|
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, &ds4->TargetMacAddress, sizeof(MAC_ADDRESS));
|
|
// Adjust byte order
|
|
ReverseByteArray(Response + 1, sizeof(MAC_ADDRESS));
|
|
|
|
// Insert (auto-generated) host MAC address into response
|
|
RtlCopyBytes(Response + 10, &ds4->HostMacAddress, sizeof(MAC_ADDRESS));
|
|
// Adjust byte order
|
|
ReverseByteArray(Response + 10, sizeof(MAC_ADDRESS));
|
|
|
|
pRequest->TransferBufferLength = HID_GET_FEATURE_REPORT_MAC_ADDRESSES_SIZE;
|
|
RtlCopyBytes(pRequest->TransferBuffer, Response, HID_GET_FEATURE_REPORT_MAC_ADDRESSES_SIZE);
|
|
|
|
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);
|
|
|
|
TraceEvents(TRACE_LEVEL_VERBOSE,
|
|
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[HID_SET_FEATURE_REPORT_SIZE_0] =
|
|
{
|
|
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 = HID_SET_FEATURE_REPORT_SIZE_0;
|
|
RtlCopyBytes(pRequest->TransferBuffer, Response, HID_SET_FEATURE_REPORT_SIZE_0);
|
|
|
|
break;
|
|
}
|
|
case HID_REPORT_ID_4:
|
|
{
|
|
// Source: http://eleccelerator.com/wiki/index.php?title=DualShock_4#Class_Requests
|
|
UCHAR Response[HID_SET_FEATURE_REPORT_SIZE_1] =
|
|
{
|
|
0x14, 0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
0x00
|
|
};
|
|
|
|
pRequest->TransferBufferLength = HID_SET_FEATURE_REPORT_SIZE_1;
|
|
RtlCopyBytes(pRequest->TransferBuffer, Response, HID_SET_FEATURE_REPORT_SIZE_1);
|
|
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
return STATUS_SUCCESS;
|
|
}
|
|
|
|
//
|
|
// Returns interface HID report descriptor.
|
|
//
|
|
NTSTATUS UsbPdo_GetDescriptorFromInterface(PURB urb, PPDO_DEVICE_DATA pCommon)
|
|
{
|
|
NTSTATUS status = STATUS_INVALID_PARAMETER;
|
|
UCHAR Ds4HidReportDescriptor[DS4_HID_REPORT_DESCRIPTOR_SIZE] =
|
|
{
|
|
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;
|
|
|
|
TraceEvents(TRACE_LEVEL_VERBOSE,
|
|
TRACE_USBPDO,
|
|
">> >> >> _URB_CONTROL_DESCRIPTOR_REQUEST: Buffer Length %d",
|
|
pRequest->TransferBufferLength);
|
|
|
|
switch (pCommon->TargetType)
|
|
{
|
|
case DualShock4Wired:
|
|
{
|
|
if (pRequest->TransferBufferLength >= DS4_HID_REPORT_DESCRIPTOR_SIZE)
|
|
{
|
|
RtlCopyMemory(pRequest->TransferBuffer, Ds4HidReportDescriptor, DS4_HID_REPORT_DESCRIPTOR_SIZE);
|
|
status = STATUS_SUCCESS;
|
|
}
|
|
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|