Rémi Bernon : winebus.sys: Process quirky DS4 bluetooth reports.

Alexandre Julliard julliard at winehq.org
Mon Oct 18 16:16:30 CDT 2021


Module: wine
Branch: master
Commit: b51c982de16c90cc35b32609a5a778ed8073a3e5
URL:    https://source.winehq.org/git/wine.git/?a=commit;h=b51c982de16c90cc35b32609a5a778ed8073a3e5

Author: Rémi Bernon <rbernon at codeweavers.com>
Date:   Mon Oct 18 17:16:45 2021 +0200

winebus.sys: Process quirky DS4 bluetooth reports.

Based on a Proton patch from Andrew Eikum <aeikum at codeweavers.com>.

Signed-off-by: Rémi Bernon <rbernon at codeweavers.com>
Signed-off-by: Alexandre Julliard <julliard at winehq.org>

---

 dlls/winebus.sys/bus_udev.c     | 33 +++++++++++++++++++++++++++++++--
 dlls/winebus.sys/unix_private.h |  1 +
 dlls/winebus.sys/unixlib.c      |  9 +++++++++
 3 files changed, 41 insertions(+), 2 deletions(-)

diff --git a/dlls/winebus.sys/bus_udev.c b/dlls/winebus.sys/bus_udev.c
index e137299df77..1dfce05e4bc 100644
--- a/dlls/winebus.sys/bus_udev.c
+++ b/dlls/winebus.sys/bus_udev.c
@@ -120,9 +120,12 @@ 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
+
 struct hidraw_device
 {
     struct base_device base;
+    DWORD quirks;
 };
 
 static inline struct hidraw_device *hidraw_impl_from_unix_device(struct unix_device *iface)
@@ -282,7 +285,7 @@ static NTSTATUS hidraw_device_get_report_descriptor(struct unix_device *iface, B
 static void hidraw_device_read_report(struct unix_device *iface)
 {
     struct hidraw_device *impl = hidraw_impl_from_unix_device(iface);
-    BYTE report_buffer[1024];
+    BYTE report_buffer[1024], *buff = report_buffer;
 
     int size = read(impl->base.device_fd, report_buffer, sizeof(report_buffer));
     if (size == -1)
@@ -290,7 +293,26 @@ static void hidraw_device_read_report(struct unix_device *iface)
     else if (size == 0)
         TRACE_(hid_report)("Failed to read report\n");
     else
-        bus_event_queue_input_report(&event_queue, iface, report_buffer, size);
+    {
+        /* 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;
+        }
+
+        bus_event_queue_input_report(&event_queue, iface, buff, size);
+    }
 }
 
 static void hidraw_device_set_output_report(struct unix_device *iface, HID_XFER_PACKET *packet, IO_STATUS_BLOCK *io)
@@ -1149,6 +1171,12 @@ 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;
+}
+
 static void udev_add_device(struct udev_device *dev)
 {
     struct device_desc desc =
@@ -1250,6 +1278,7 @@ static void udev_add_device(struct udev_device *dev)
         impl->read_report = hidraw_device_read_report;
         impl->udev_device = udev_device_ref(dev);
         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);
     }
diff --git a/dlls/winebus.sys/unix_private.h b/dlls/winebus.sys/unix_private.h
index 61c6dbb1deb..89a0c9abad9 100644
--- a/dlls/winebus.sys/unix_private.h
+++ b/dlls/winebus.sys/unix_private.h
@@ -244,5 +244,6 @@ extern BOOL hid_device_sync_report(struct unix_device *iface) DECLSPEC_HIDDEN;
 extern void hid_device_drop_report(struct unix_device *iface) DECLSPEC_HIDDEN;
 
 BOOL is_xbox_gamepad(WORD vid, WORD pid) DECLSPEC_HIDDEN;
+BOOL is_dualshock4_gamepad(WORD vid, WORD pid) DECLSPEC_HIDDEN;
 
 #endif /* __WINEBUS_UNIX_PRIVATE_H */
diff --git a/dlls/winebus.sys/unixlib.c b/dlls/winebus.sys/unixlib.c
index f66fd78d176..6cf58570c76 100644
--- a/dlls/winebus.sys/unixlib.c
+++ b/dlls/winebus.sys/unixlib.c
@@ -61,6 +61,15 @@ BOOL is_xbox_gamepad(WORD vid, WORD pid)
     return FALSE;
 }
 
+BOOL is_dualshock4_gamepad(WORD vid, WORD pid)
+{
+    if (vid != 0x054c) return FALSE;
+    if (pid == 0x05c4) return TRUE; /* DualShock 4 [CUH-ZCT1x] */
+    if (pid == 0x09cc) return TRUE; /* DualShock 4 [CUH-ZCT2x] */
+    if (pid == 0x0ba0) return TRUE; /* Dualshock 4 Wireless Adaptor */
+    return FALSE;
+}
+
 struct mouse_device
 {
     struct unix_device unix_device;




More information about the wine-cvs mailing list