HID: playstation: track devices in list.
authorRoderick Colenbrander <roderick.colenbrander@sony.com>
Sun, 7 Feb 2021 21:49:01 +0000 (13:49 -0800)
committerBenjamin Tissoires <benjamin.tissoires@redhat.com>
Thu, 11 Feb 2021 15:57:18 +0000 (16:57 +0100)
Track devices in a list, so we can detect when a device is connected
twice when using Bluetooth and USB.

Signed-off-by: Roderick Colenbrander <roderick.colenbrander@sony.com>
Reviewed-by: Barnabás Pőcze <pobrn@protonmail.com>
Signed-off-by: Benjamin Tissoires <benjamin.tissoires@redhat.com>
drivers/hid/hid-playstation.c

index b09ec604cd27f564cef3f5df1c4771fec7c9f8d4..20fe29fc61c097f328fe2a7cdb7b004b8d0af855 100644 (file)
 
 #include "hid-ids.h"
 
+/* List of connected playstation devices. */
+static DEFINE_MUTEX(ps_devices_lock);
+static LIST_HEAD(ps_devices_list);
+
 #define HID_PLAYSTATION_VERSION_PATCH 0x8000
 
 /* Base class for playstation devices. */
 struct ps_device {
+       struct list_head list;
        struct hid_device *hdev;
        spinlock_t lock;
 
@@ -160,6 +165,38 @@ static const struct {int x; int y; } ps_gamepad_hat_mapping[] = {
        {0, 0},
 };
 
+/*
+ * Add a new ps_device to ps_devices if it doesn't exist.
+ * Return error on duplicate device, which can happen if the same
+ * device is connected using both Bluetooth and USB.
+ */
+static int ps_devices_list_add(struct ps_device *dev)
+{
+       struct ps_device *entry;
+
+       mutex_lock(&ps_devices_lock);
+       list_for_each_entry(entry, &ps_devices_list, list) {
+               if (!memcmp(entry->mac_address, dev->mac_address, sizeof(dev->mac_address))) {
+                       hid_err(dev->hdev, "Duplicate device found for MAC address %pMR.\n",
+                                       dev->mac_address);
+                       mutex_unlock(&ps_devices_lock);
+                       return -EEXIST;
+               }
+       }
+
+       list_add_tail(&dev->list, &ps_devices_list);
+       mutex_unlock(&ps_devices_lock);
+       return 0;
+}
+
+static int ps_devices_list_remove(struct ps_device *dev)
+{
+       mutex_lock(&ps_devices_lock);
+       list_del(&dev->list);
+       mutex_unlock(&ps_devices_lock);
+       return 0;
+}
+
 static struct input_dev *ps_allocate_input_dev(struct hid_device *hdev, const char *name_suffix)
 {
        struct input_dev *input_dev;
@@ -675,6 +712,10 @@ static struct ps_device *dualsense_create(struct hid_device *hdev)
        }
        snprintf(hdev->uniq, sizeof(hdev->uniq), "%pMR", ds->base.mac_address);
 
+       ret = ps_devices_list_add(ps_dev);
+       if (ret)
+               return ERR_PTR(ret);
+
        ret = dualsense_get_calibration_data(ds);
        if (ret) {
                hid_err(hdev, "Failed to get calibration data from DualSense\n");
@@ -707,6 +748,7 @@ static struct ps_device *dualsense_create(struct hid_device *hdev)
        return &ds->base;
 
 err:
+       ps_devices_list_remove(ps_dev);
        return ERR_PTR(ret);
 }
 
@@ -764,6 +806,10 @@ err_stop:
 
 static void ps_remove(struct hid_device *hdev)
 {
+       struct ps_device *dev = hid_get_drvdata(hdev);
+
+       ps_devices_list_remove(dev);
+
        hid_hw_close(hdev);
        hid_hw_stop(hdev);
 }