1
0
mirror of https://github.com/wine-mirror/wine synced 2024-06-29 06:14:34 +00:00

winebus: Move Sony controllers report fixups to PE side.

Wine-Bug: https://bugs.winehq.org/show_bug.cgi?id=56602
This commit is contained in:
Rémi Bernon 2024-05-10 16:21:00 +02:00 committed by Alexandre Julliard
parent 4ddee5a092
commit c708295ed6
2 changed files with 92 additions and 87 deletions

View File

@ -116,13 +116,9 @@ static inline struct base_device *impl_from_unix_device(struct unix_device *ifac
return CONTAINING_RECORD(iface, struct base_device, unix_device);
}
#define QUIRK_DS4_BT 0x1
#define QUIRK_DUALSENSE_BT 0x2
struct hidraw_device
{
struct base_device base;
DWORD quirks;
};
static inline struct hidraw_device *hidraw_impl_from_unix_device(struct unix_device *iface)
@ -339,77 +335,7 @@ static void hidraw_device_read_report(struct unix_device *iface)
else if (size == 0)
TRACE("Failed to read report\n");
else
{
/* As described in the Linux kernel driver, when connected over bluetooth, DS4 controllers
* start sending input through report #17 as soon as they receive a feature report #2, which
* the kernel sends anyway for calibration.
*
* Input report #17 is the same as the default input report #1, with additional gyro data and
* two additional bytes in front, but is only described as vendor specific in the report descriptor,
* and applications aren't expecting it.
*
* We have to translate it to input report #1, like native driver does.
*/
if ((impl->quirks & QUIRK_DS4_BT) && report_buffer[0] == 0x11 && size >= 12)
{
size = 10;
buff += 2;
buff[0] = 1;
}
/* The behavior of DualSense is very similar to DS4 described above with a few exceptions.
*
* The report number #41 is used for the extended bluetooth input report. The report comes
* with only one extra byte in front and the format is not exactly the same as the one used
* for the report #1 so we need to shuffle a few bytes around.
*
* Basic #1 report:
* X Y Z RZ Buttons[3] TriggerLeft TriggerRight
*
* Extended #41 report:
* Prefix X Y Z Rz TriggerLeft TriggerRight Counter Buttons[3] ...
*/
if ((impl->quirks & QUIRK_DUALSENSE_BT) && report_buffer[0] == 0x31 && size >= 11)
{
BYTE trigger[2];
size = 10;
buff += 1;
buff[0] = 1; /* fake report #1 */
trigger[0] = buff[5]; /* TriggerLeft*/
trigger[1] = buff[6]; /* TriggerRight */
buff[5] = buff[8]; /* Buttons[0] */
buff[6] = buff[9]; /* Buttons[1] */
buff[7] = buff[10]; /* Buttons[2] */
buff[8] = trigger[0]; /* TriggerLeft */
buff[9] = trigger[1]; /* TirggerRight */
}
bus_event_queue_input_report(&event_queue, iface, buff, size);
}
}
static void hidraw_disable_sony_quirks(struct unix_device *iface)
{
struct hidraw_device *impl = hidraw_impl_from_unix_device(iface);
/* FIXME: we may want to validate CRC at the end of the outbound HID reports,
* as controllers do not switch modes if it is incorrect.
*/
if ((impl->quirks & QUIRK_DS4_BT))
{
TRACE("Disabling report quirk for Bluetooth DualShock4 controller iface %p\n", iface);
impl->quirks &= ~QUIRK_DS4_BT;
}
if ((impl->quirks & QUIRK_DUALSENSE_BT))
{
TRACE("Disabling report quirk for Bluetooth DualSense controller iface %p\n", iface);
impl->quirks &= ~QUIRK_DUALSENSE_BT;
}
}
static void hidraw_device_set_output_report(struct unix_device *iface, HID_XFER_PACKET *packet, IO_STATUS_BLOCK *io)
@ -431,7 +357,6 @@ static void hidraw_device_set_output_report(struct unix_device *iface, HID_XFER_
if (count > 0)
{
hidraw_disable_sony_quirks(iface);
io->Information = count;
io->Status = STATUS_SUCCESS;
}
@ -464,7 +389,6 @@ static void hidraw_device_get_feature_report(struct unix_device *iface, HID_XFER
if (count > 0)
{
hidraw_disable_sony_quirks(iface);
io->Information = count;
io->Status = STATUS_SUCCESS;
}
@ -501,7 +425,6 @@ static void hidraw_device_set_feature_report(struct unix_device *iface, HID_XFER
if (count > 0)
{
hidraw_disable_sony_quirks(iface);
io->Information = count;
io->Status = STATUS_SUCCESS;
}
@ -1275,15 +1198,6 @@ static void get_device_subsystem_info(struct udev_device *dev, char const *subsy
ntdll_umbstowcs(tmp, strlen(tmp) + 1, desc->serialnumber, ARRAY_SIZE(desc->serialnumber));
}
static void hidraw_set_quirks(struct hidraw_device *impl, DWORD bus_type, WORD vid, WORD pid)
{
if (bus_type == BUS_BLUETOOTH && is_dualshock4_gamepad(vid, pid))
impl->quirks |= QUIRK_DS4_BT;
if (bus_type == BUS_BLUETOOTH && is_dualsense_gamepad(vid, pid))
impl->quirks |= QUIRK_DUALSENSE_BT;
}
static void udev_add_device(struct udev_device *dev, int fd)
{
struct device_desc desc =
@ -1387,7 +1301,6 @@ static void udev_add_device(struct udev_device *dev, int fd)
impl->udev_device = udev_device_ref(dev);
strcpy(impl->devnode, devnode);
impl->device_fd = fd;
hidraw_set_quirks((struct hidraw_device *)impl, bus, desc.vid, desc.pid);
bus_event_queue_device_created(&event_queue, &impl->unix_device, &desc);
}

View File

@ -67,6 +67,9 @@ enum device_state
DEVICE_STATE_REMOVED,
};
#define HIDRAW_FIXUP_DUALSHOCK_BT 0x1
#define HIDRAW_FIXUP_DUALSENSE_BT 0x2
struct device_extension
{
struct list entry;
@ -86,6 +89,7 @@ struct device_extension
struct list reports;
IRP *pending_read;
UINT32 report_fixups;
UINT64 unix_device;
};
@ -302,6 +306,17 @@ static DEVICE_OBJECT *bus_create_hid_device(struct device_desc *desc, UINT64 uni
ext->unix_device = unix_device;
list_init(&ext->reports);
if (desc->is_hidraw && desc->is_bluetooth && is_dualshock4_gamepad(desc->vid, desc->pid))
{
TRACE("Enabling report fixup for Bluetooth DualShock4 device %p\n", device);
ext->report_fixups |= HIDRAW_FIXUP_DUALSHOCK_BT;
}
if (desc->is_hidraw && desc->is_bluetooth && is_dualsense_gamepad(desc->vid, desc->pid))
{
TRACE("Enabling report fixup for Bluetooth DualSense device %p\n", device);
ext->report_fixups |= HIDRAW_FIXUP_DUALSENSE_BT;
}
InitializeCriticalSectionEx(&ext->cs, 0, RTL_CRITICAL_SECTION_FLAG_FORCE_DEBUG_INFO);
ext->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": cs");
@ -468,6 +483,59 @@ static void process_hid_report(DEVICE_OBJECT *device, BYTE *report_buf, DWORD re
memcpy(report->buffer, report_buf, report_len);
report->length = report_len;
if (ext->report_fixups & HIDRAW_FIXUP_DUALSHOCK_BT)
{
/* As described in the Linux kernel driver, when connected over bluetooth, DS4 controllers
* start sending input through report #17 as soon as they receive a feature report #2, which
* the kernel sends anyway for calibration.
*
* Input report #17 is the same as the default input report #1, with additional gyro data and
* two additional bytes in front, but is only described as vendor specific in the report descriptor,
* and applications aren't expecting it.
*
* We have to translate it to input report #1, like native driver does.
*/
if (report->buffer[0] == 0x11 && report->length >= 12)
{
memmove(report->buffer, report->buffer + 2, 10);
report->buffer[0] = 1; /* fake report #1 */
report->length = 10;
}
}
if (ext->report_fixups & HIDRAW_FIXUP_DUALSENSE_BT)
{
/* The behavior of DualSense is very similar to DS4 described above with a few exceptions.
*
* The report number #41 is used for the extended bluetooth input report. The report comes
* with only one extra byte in front and the format is not exactly the same as the one used
* for the report #1 so we need to shuffle a few bytes around.
*
* Basic #1 report:
* X Y Z RZ Buttons[3] TriggerLeft TriggerRight
*
* Extended #41 report:
* Prefix X Y Z Rz TriggerLeft TriggerRight Counter Buttons[3] ...
*/
if (report->buffer[0] == 0x31 && report->length >= 11)
{
BYTE trigger[2];
memmove(report->buffer, report->buffer + 1, 10);
report->buffer[0] = 1; /* fake report #1 */
report->length = 10;
trigger[0] = report->buffer[5]; /* TriggerLeft*/
trigger[1] = report->buffer[6]; /* TriggerRight */
report->buffer[5] = report->buffer[8]; /* Buttons[0] */
report->buffer[6] = report->buffer[9]; /* Buttons[1] */
report->buffer[7] = report->buffer[10]; /* Buttons[2] */
report->buffer[8] = trigger[0]; /* TriggerLeft */
report->buffer[9] = trigger[1]; /* TirggerRight */
}
}
RtlEnterCriticalSection(&ext->cs);
list_add_tail(&ext->reports, &report->entry);
@ -1059,6 +1127,27 @@ static NTSTATUS hid_get_device_string(DEVICE_OBJECT *device, DWORD index, WCHAR
return STATUS_NOT_IMPLEMENTED;
}
static void hidraw_disable_report_fixups(DEVICE_OBJECT *device)
{
struct device_extension *ext = (struct device_extension *)device->DeviceExtension;
/* FIXME: we may want to validate CRC at the end of the outbound HID reports,
* as controllers do not switch modes if it is incorrect.
*/
if ((ext->report_fixups & HIDRAW_FIXUP_DUALSHOCK_BT))
{
TRACE("Disabling report fixup for Bluetooth DualShock4 device %p\n", device);
ext->report_fixups &= ~HIDRAW_FIXUP_DUALSHOCK_BT;
}
if ((ext->report_fixups & HIDRAW_FIXUP_DUALSENSE_BT))
{
TRACE("Disabling report fixup for Bluetooth DualSense device %p\n", device);
ext->report_fixups &= ~HIDRAW_FIXUP_DUALSENSE_BT;
}
}
static NTSTATUS WINAPI hid_internal_dispatch(DEVICE_OBJECT *device, IRP *irp)
{
IO_STACK_LOCATION *irpsp = IoGetCurrentIrpStackLocation(irp);
@ -1195,12 +1284,14 @@ static NTSTATUS WINAPI hid_internal_dispatch(DEVICE_OBJECT *device, IRP *irp)
}
}
unix_device_set_output_report(device, packet, &irp->IoStatus);
if (!irp->IoStatus.Status) hidraw_disable_report_fixups(device);
break;
}
case IOCTL_HID_GET_FEATURE:
{
HID_XFER_PACKET *packet = (HID_XFER_PACKET *)irp->UserBuffer;
unix_device_get_feature_report(device, packet, &irp->IoStatus);
if (!irp->IoStatus.Status) hidraw_disable_report_fixups(device);
if (!irp->IoStatus.Status && TRACE_ON(hid))
{
TRACE("read feature report id %u length %lu:\n", packet->reportId, packet->reportBufferLen);
@ -1231,6 +1322,7 @@ static NTSTATUS WINAPI hid_internal_dispatch(DEVICE_OBJECT *device, IRP *irp)
}
}
unix_device_set_feature_report(device, packet, &irp->IoStatus);
if (!irp->IoStatus.Status) hidraw_disable_report_fixups(device);
break;
}
default: