@@ -315,6 +315,11 @@ struct dualsense_output_report {
315315#define DS4_STATUS0_CABLE_STATE BIT(4)
316316/* Battery status within batery_status field. */
317317#define DS4_BATTERY_STATUS_FULL 11
318+ /* Status1 bit2 contains dongle connection state:
319+ * 0 = connectd
320+ * 1 = disconnected
321+ */
322+ #define DS4_STATUS1_DONGLE_STATE BIT(2)
318323
319324/* The lower 6 bits of hw_control of the Bluetooth main output report
320325 * control the interval at which Dualshock 4 reports data:
@@ -344,6 +349,13 @@ struct dualsense_output_report {
344349#define DS4_TOUCHPAD_WIDTH 1920
345350#define DS4_TOUCHPAD_HEIGHT 942
346351
352+ enum dualshock4_dongle_state {
353+ DONGLE_DISCONNECTED ,
354+ DONGLE_CALIBRATING ,
355+ DONGLE_CONNECTED ,
356+ DONGLE_DISABLED
357+ };
358+
347359struct dualshock4 {
348360 struct ps_device base ;
349361 struct input_dev * gamepad ;
@@ -354,6 +366,11 @@ struct dualshock4 {
354366 struct ps_calibration_data accel_calib_data [3 ];
355367 struct ps_calibration_data gyro_calib_data [3 ];
356368
369+ /* Only used on dongle to track state transitions. */
370+ enum dualshock4_dongle_state dongle_state ;
371+ /* Used during calibration. */
372+ struct work_struct dongle_hotplug_worker ;
373+
357374 /* Timestamp for sensor data */
358375 bool sensor_timestamp_initialized ;
359376 uint32_t prev_sensor_timestamp ;
@@ -513,9 +530,11 @@ static const struct {int x; int y; } ps_gamepad_hat_mapping[] = {
513530 {0 , 0 },
514531};
515532
533+ static int dualshock4_get_calibration_data (struct dualshock4 * ds4 );
516534static inline void dualsense_schedule_work (struct dualsense * ds );
517535static inline void dualshock4_schedule_work (struct dualshock4 * ds4 );
518536static void dualsense_set_lightbar (struct dualsense * ds , uint8_t red , uint8_t green , uint8_t blue );
537+ static void dualshock4_set_default_lightbar_colors (struct dualshock4 * ds4 );
519538
520539/*
521540 * Add a new ps_device to ps_devices if it doesn't exist.
@@ -1678,6 +1697,33 @@ static struct ps_device *dualsense_create(struct hid_device *hdev)
16781697 return ERR_PTR (ret );
16791698}
16801699
1700+ static void dualshock4_dongle_calibration_work (struct work_struct * work )
1701+ {
1702+ struct dualshock4 * ds4 = container_of (work , struct dualshock4 , dongle_hotplug_worker );
1703+ unsigned long flags ;
1704+ enum dualshock4_dongle_state dongle_state ;
1705+ int ret ;
1706+
1707+ ret = dualshock4_get_calibration_data (ds4 );
1708+ if (ret < 0 ) {
1709+ /* This call is very unlikely to fail for the dongle. When it
1710+ * fails we are probably in a very bad state, so mark the
1711+ * dongle as disabled. We will re-enable the dongle if a new
1712+ * DS4 hotplug is detect from sony_raw_event as any issues
1713+ * are likely resolved then (the dongle is quite stupid).
1714+ */
1715+ hid_err (ds4 -> base .hdev , "DualShock 4 USB dongle: calibration failed, disabling device\n" );
1716+ dongle_state = DONGLE_DISABLED ;
1717+ } else {
1718+ hid_info (ds4 -> base .hdev , "DualShock 4 USB dongle: calibration completed\n" );
1719+ dongle_state = DONGLE_CONNECTED ;
1720+ }
1721+
1722+ spin_lock_irqsave (& ds4 -> base .lock , flags );
1723+ ds4 -> dongle_state = dongle_state ;
1724+ spin_unlock_irqrestore (& ds4 -> base .lock , flags );
1725+ }
1726+
16811727static int dualshock4_get_calibration_data (struct dualshock4 * ds4 )
16821728{
16831729 struct hid_device * hdev = ds4 -> base .hdev ;
@@ -1694,15 +1740,34 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4)
16941740 uint8_t * buf ;
16951741
16961742 if (ds4 -> base .hdev -> bus == BUS_USB ) {
1743+ int retries ;
1744+
16971745 buf = kzalloc (DS4_FEATURE_REPORT_CALIBRATION_SIZE , GFP_KERNEL );
16981746 if (!buf )
16991747 return - ENOMEM ;
17001748
1701- ret = ps_get_report (hdev , DS4_FEATURE_REPORT_CALIBRATION , buf ,
1702- DS4_FEATURE_REPORT_CALIBRATION_SIZE , true);
1703- if (ret ) {
1704- hid_err (hdev , "Failed to retrieve DualShock4 calibration info: %d\n" , ret );
1705- goto err_free ;
1749+ /* We should normally receive the feature report data we asked
1750+ * for, but hidraw applications such as Steam can issue feature
1751+ * reports as well. In particular for Dongle reconnects, Steam
1752+ * and this function are competing resulting in often receiving
1753+ * data for a different HID report, so retry a few times.
1754+ */
1755+ for (retries = 0 ; retries < 3 ; retries ++ ) {
1756+ ret = ps_get_report (hdev , DS4_FEATURE_REPORT_CALIBRATION , buf ,
1757+ DS4_FEATURE_REPORT_CALIBRATION_SIZE , true);
1758+ if (ret ) {
1759+ if (retries < 2 ) {
1760+ hid_warn (hdev , "Retrying DualShock 4 get calibration report (0x02) request\n" );
1761+ continue ;
1762+ } else {
1763+ ret = - EILSEQ ;
1764+ goto err_free ;
1765+ }
1766+ hid_err (hdev , "Failed to retrieve DualShock4 calibration info: %d\n" , ret );
1767+ goto err_free ;
1768+ } else {
1769+ break ;
1770+ }
17061771 }
17071772 } else { /* Bluetooth */
17081773 buf = kzalloc (DS4_FEATURE_REPORT_CALIBRATION_BT_SIZE , GFP_KERNEL );
@@ -2220,6 +2285,62 @@ static int dualshock4_parse_report(struct ps_device *ps_dev, struct hid_report *
22202285 return 0 ;
22212286}
22222287
2288+ static int dualshock4_dongle_parse_report (struct ps_device * ps_dev , struct hid_report * report ,
2289+ u8 * data , int size )
2290+ {
2291+ struct dualshock4 * ds4 = container_of (ps_dev , struct dualshock4 , base );
2292+ bool connected = false;
2293+
2294+ /* The dongle reports data using the main USB report (0x1) no matter whether a controller
2295+ * is connected with mostly zeros. The report does contain dongle status, which we use to
2296+ * determine if a controller is connected and if so we forward to the regular DualShock4
2297+ * parsing code.
2298+ */
2299+ if (data [0 ] == DS4_INPUT_REPORT_USB && size == DS4_INPUT_REPORT_USB_SIZE ) {
2300+ struct dualshock4_input_report_common * ds4_report = (struct dualshock4_input_report_common * )& data [1 ];
2301+ unsigned long flags ;
2302+
2303+ connected = ds4_report -> status [1 ] & DS4_STATUS1_DONGLE_STATE ? false : true;
2304+
2305+ if (ds4 -> dongle_state == DONGLE_DISCONNECTED && connected ) {
2306+ hid_info (ps_dev -> hdev , "DualShock 4 USB dongle: controller connected\n" );
2307+
2308+ dualshock4_set_default_lightbar_colors (ds4 );
2309+
2310+ spin_lock_irqsave (& ps_dev -> lock , flags );
2311+ ds4 -> dongle_state = DONGLE_CALIBRATING ;
2312+ spin_unlock_irqrestore (& ps_dev -> lock , flags );
2313+
2314+ schedule_work (& ds4 -> dongle_hotplug_worker );
2315+
2316+ /* Don't process the report since we don't have
2317+ * calibration data, but let hidraw have it anyway.
2318+ */
2319+ return 0 ;
2320+ } else if ((ds4 -> dongle_state == DONGLE_CONNECTED ||
2321+ ds4 -> dongle_state == DONGLE_DISABLED ) && !connected ) {
2322+ hid_info (ps_dev -> hdev , "DualShock 4 USB dongle: controller disconnected\n" );
2323+
2324+ spin_lock_irqsave (& ps_dev -> lock , flags );
2325+ ds4 -> dongle_state = DONGLE_DISCONNECTED ;
2326+ spin_unlock_irqrestore (& ps_dev -> lock , flags );
2327+
2328+ /* Return 0, so hidraw can get the report. */
2329+ return 0 ;
2330+ } else if (ds4 -> dongle_state == DONGLE_CALIBRATING ||
2331+ ds4 -> dongle_state == DONGLE_DISABLED ||
2332+ ds4 -> dongle_state == DONGLE_DISCONNECTED ) {
2333+ /* Return 0, so hidraw can get the report. */
2334+ return 0 ;
2335+ }
2336+ }
2337+
2338+ if (connected )
2339+ return dualshock4_parse_report (ps_dev , report , data , size );
2340+
2341+ return 0 ;
2342+ }
2343+
22232344static int dualshock4_play_effect (struct input_dev * dev , void * data , struct ff_effect * effect )
22242345{
22252346 struct hid_device * hdev = input_get_drvdata (dev );
@@ -2249,6 +2370,9 @@ static void dualshock4_remove(struct ps_device *ps_dev)
22492370 spin_unlock_irqrestore (& ds4 -> base .lock , flags );
22502371
22512372 cancel_work_sync (& ds4 -> output_worker );
2373+
2374+ if (ps_dev -> hdev -> product == USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE )
2375+ cancel_work_sync (& ds4 -> dongle_hotplug_worker );
22522376}
22532377
22542378static inline void dualshock4_schedule_work (struct dualshock4 * ds4 )
@@ -2342,6 +2466,14 @@ static struct ps_device *dualshock4_create(struct hid_device *hdev)
23422466 if (!ds4 -> output_report_dmabuf )
23432467 return ERR_PTR (- ENOMEM );
23442468
2469+ if (hdev -> product == USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE ) {
2470+ ds4 -> dongle_state = DONGLE_DISCONNECTED ;
2471+ INIT_WORK (& ds4 -> dongle_hotplug_worker , dualshock4_dongle_calibration_work );
2472+
2473+ /* Override parse report for dongle specific hotplug handling. */
2474+ ps_dev -> parse_report = dualshock4_dongle_parse_report ;
2475+ }
2476+
23452477 ret = dualshock4_get_mac_address (ds4 );
23462478 if (ret ) {
23472479 hid_err (hdev , "Failed to get MAC address from DualShock4\n" );
@@ -2457,7 +2589,8 @@ static int ps_probe(struct hid_device *hdev, const struct hid_device_id *id)
24572589 }
24582590
24592591 if (hdev -> product == USB_DEVICE_ID_SONY_PS4_CONTROLLER ||
2460- hdev -> product == USB_DEVICE_ID_SONY_PS4_CONTROLLER_2 ) {
2592+ hdev -> product == USB_DEVICE_ID_SONY_PS4_CONTROLLER_2 ||
2593+ hdev -> product == USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE ) {
24612594 dev = dualshock4_create (hdev );
24622595 if (IS_ERR (dev )) {
24632596 hid_err (hdev , "Failed to create dualshock4.\n" );
@@ -2503,6 +2636,7 @@ static const struct hid_device_id ps_devices[] = {
25032636 { HID_USB_DEVICE (USB_VENDOR_ID_SONY , USB_DEVICE_ID_SONY_PS4_CONTROLLER ) },
25042637 { HID_BLUETOOTH_DEVICE (USB_VENDOR_ID_SONY , USB_DEVICE_ID_SONY_PS4_CONTROLLER_2 ) },
25052638 { HID_USB_DEVICE (USB_VENDOR_ID_SONY , USB_DEVICE_ID_SONY_PS4_CONTROLLER_2 ) },
2639+ { HID_USB_DEVICE (USB_VENDOR_ID_SONY , USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE ) },
25062640 /* Sony DualSense controllers for PS5 */
25072641 { HID_BLUETOOTH_DEVICE (USB_VENDOR_ID_SONY , USB_DEVICE_ID_SONY_PS5_CONTROLLER ) },
25082642 { HID_USB_DEVICE (USB_VENDOR_ID_SONY , USB_DEVICE_ID_SONY_PS5_CONTROLLER ) },
0 commit comments