Files
ViGEmBus/sys/usbpdo.c
2018-05-13 20:54:29 +02:00

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, &notifyRequest);
if (NT_SUCCESS(status))
{
PXUSB_REQUEST_NOTIFICATION notify = NULL;
status = WdfRequestRetrieveOutputBuffer(notifyRequest, sizeof(XUSB_REQUEST_NOTIFICATION), (PVOID)&notify, 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, &notifyRequest);
if (NT_SUCCESS(status))
{
PDS4_REQUEST_NOTIFICATION notify = NULL;
status = WdfRequestRetrieveOutputBuffer(notifyRequest, sizeof(DS4_REQUEST_NOTIFICATION), (PVOID)&notify, 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;
}