summaryrefslogtreecommitdiff
path: root/drivers/hid
diff options
context:
space:
mode:
authorRoderick Colenbrander <roderick.colenbrander@sony.com>2021-02-07 13:49:01 -0800
committerBenjamin Tissoires <benjamin.tissoires@redhat.com>2021-02-11 16:57:18 +0100
commit53f04e83577c5e146eeee1a671efeb58db14afd1 (patch)
tree7f4e0df417287e425a5f9c457f6edaa4f5273cd0 /drivers/hid
parent402987c5d98a9dd0d611cfe3af5e5bdc13d122d0 (diff)
HID: playstation: track devices in list.
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>
Diffstat (limited to 'drivers/hid')
-rw-r--r--drivers/hid/hid-playstation.c46
1 files changed, 46 insertions, 0 deletions
diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c
index b09ec604cd27..20fe29fc61c0 100644
--- a/drivers/hid/hid-playstation.c
+++ b/drivers/hid/hid-playstation.c
@@ -15,10 +15,15 @@
#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);
}