|
@@ -33,6 +33,7 @@
|
|
#include <linux/leds.h>
|
|
#include <linux/leds.h>
|
|
#include <linux/power_supply.h>
|
|
#include <linux/power_supply.h>
|
|
#include <linux/spinlock.h>
|
|
#include <linux/spinlock.h>
|
|
|
|
+#include <linux/list.h>
|
|
#include <linux/input/mt.h>
|
|
#include <linux/input/mt.h>
|
|
|
|
|
|
#include "hid-ids.h"
|
|
#include "hid-ids.h"
|
|
@@ -717,8 +718,12 @@ static enum power_supply_property sony_battery_props[] = {
|
|
POWER_SUPPLY_PROP_STATUS,
|
|
POWER_SUPPLY_PROP_STATUS,
|
|
};
|
|
};
|
|
|
|
|
|
|
|
+static spinlock_t sony_dev_list_lock;
|
|
|
|
+static LIST_HEAD(sony_device_list);
|
|
|
|
+
|
|
struct sony_sc {
|
|
struct sony_sc {
|
|
spinlock_t lock;
|
|
spinlock_t lock;
|
|
|
|
+ struct list_head list_node;
|
|
struct hid_device *hdev;
|
|
struct hid_device *hdev;
|
|
struct led_classdev *leds[MAX_LEDS];
|
|
struct led_classdev *leds[MAX_LEDS];
|
|
unsigned long quirks;
|
|
unsigned long quirks;
|
|
@@ -730,6 +735,7 @@ struct sony_sc {
|
|
__u8 right;
|
|
__u8 right;
|
|
#endif
|
|
#endif
|
|
|
|
|
|
|
|
+ __u8 mac_address[6];
|
|
__u8 worker_initialized;
|
|
__u8 worker_initialized;
|
|
__u8 cable_state;
|
|
__u8 cable_state;
|
|
__u8 battery_charging;
|
|
__u8 battery_charging;
|
|
@@ -1489,6 +1495,133 @@ static int sony_register_touchpad(struct sony_sc *sc, int touch_count,
|
|
return 0;
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+/*
|
|
|
|
+ * If a controller is plugged in via USB while already connected via Bluetooth
|
|
|
|
+ * it will show up as two devices. A global list of connected controllers and
|
|
|
|
+ * their MAC addresses is maintained to ensure that a device is only connected
|
|
|
|
+ * once.
|
|
|
|
+ */
|
|
|
|
+static int sony_check_add_dev_list(struct sony_sc *sc)
|
|
|
|
+{
|
|
|
|
+ struct sony_sc *entry;
|
|
|
|
+ unsigned long flags;
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ spin_lock_irqsave(&sony_dev_list_lock, flags);
|
|
|
|
+
|
|
|
|
+ list_for_each_entry(entry, &sony_device_list, list_node) {
|
|
|
|
+ ret = memcmp(sc->mac_address, entry->mac_address,
|
|
|
|
+ sizeof(sc->mac_address));
|
|
|
|
+ if (!ret) {
|
|
|
|
+ ret = -EEXIST;
|
|
|
|
+ hid_info(sc->hdev, "controller with MAC address %pMR already connected\n",
|
|
|
|
+ sc->mac_address);
|
|
|
|
+ goto unlock;
|
|
|
|
+ }
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ ret = 0;
|
|
|
|
+ list_add(&(sc->list_node), &sony_device_list);
|
|
|
|
+
|
|
|
|
+unlock:
|
|
|
|
+ spin_unlock_irqrestore(&sony_dev_list_lock, flags);
|
|
|
|
+ return ret;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static void sony_remove_dev_list(struct sony_sc *sc)
|
|
|
|
+{
|
|
|
|
+ unsigned long flags;
|
|
|
|
+
|
|
|
|
+ if (sc->list_node.next) {
|
|
|
|
+ spin_lock_irqsave(&sony_dev_list_lock, flags);
|
|
|
|
+ list_del(&(sc->list_node));
|
|
|
|
+ spin_unlock_irqrestore(&sony_dev_list_lock, flags);
|
|
|
|
+ }
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int sony_get_bt_devaddr(struct sony_sc *sc)
|
|
|
|
+{
|
|
|
|
+ int ret;
|
|
|
|
+
|
|
|
|
+ /* HIDP stores the device MAC address as a string in the uniq field. */
|
|
|
|
+ ret = strlen(sc->hdev->uniq);
|
|
|
|
+ if (ret != 17)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ ret = sscanf(sc->hdev->uniq,
|
|
|
|
+ "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
|
|
|
|
+ &sc->mac_address[5], &sc->mac_address[4], &sc->mac_address[3],
|
|
|
|
+ &sc->mac_address[2], &sc->mac_address[1], &sc->mac_address[0]);
|
|
|
|
+
|
|
|
|
+ if (ret != 6)
|
|
|
|
+ return -EINVAL;
|
|
|
|
+
|
|
|
|
+ return 0;
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+static int sony_check_add(struct sony_sc *sc)
|
|
|
|
+{
|
|
|
|
+ int n, ret;
|
|
|
|
+
|
|
|
|
+ if ((sc->quirks & DUALSHOCK4_CONTROLLER_BT) ||
|
|
|
|
+ (sc->quirks & SIXAXIS_CONTROLLER_BT)) {
|
|
|
|
+ /*
|
|
|
|
+ * sony_get_bt_devaddr() attempts to parse the Bluetooth MAC
|
|
|
|
+ * address from the uniq string where HIDP stores it.
|
|
|
|
+ * As uniq cannot be guaranteed to be a MAC address in all cases
|
|
|
|
+ * a failure of this function should not prevent the connection.
|
|
|
|
+ */
|
|
|
|
+ if (sony_get_bt_devaddr(sc) < 0) {
|
|
|
|
+ hid_warn(sc->hdev, "UNIQ does not contain a MAC address; duplicate check skipped\n");
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+ } else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
|
|
|
|
+ __u8 buf[7];
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * The MAC address of a DS4 controller connected via USB can be
|
|
|
|
+ * retrieved with feature report 0x81. The address begins at
|
|
|
|
+ * offset 1.
|
|
|
|
+ */
|
|
|
|
+ ret = hid_hw_raw_request(sc->hdev, 0x81, buf, sizeof(buf),
|
|
|
|
+ HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
|
|
|
|
+
|
|
|
|
+ if (ret != 7) {
|
|
|
|
+ hid_err(sc->hdev, "failed to retrieve feature report 0x81 with the DualShock 4 MAC address\n");
|
|
|
|
+ return ret < 0 ? ret : -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ memcpy(sc->mac_address, &buf[1], sizeof(sc->mac_address));
|
|
|
|
+ } else if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
|
|
|
|
+ __u8 buf[18];
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * The MAC address of a Sixaxis controller connected via USB can
|
|
|
|
+ * be retrieved with feature report 0xf2. The address begins at
|
|
|
|
+ * offset 4.
|
|
|
|
+ */
|
|
|
|
+ ret = hid_hw_raw_request(sc->hdev, 0xf2, buf, sizeof(buf),
|
|
|
|
+ HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
|
|
|
|
+
|
|
|
|
+ if (ret != 18) {
|
|
|
|
+ hid_err(sc->hdev, "failed to retrieve feature report 0xf2 with the Sixaxis MAC address\n");
|
|
|
|
+ return ret < 0 ? ret : -EINVAL;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ /*
|
|
|
|
+ * The Sixaxis device MAC in the report is big-endian and must
|
|
|
|
+ * be byte-swapped.
|
|
|
|
+ */
|
|
|
|
+ for (n = 0; n < 6; n++)
|
|
|
|
+ sc->mac_address[5-n] = buf[4+n];
|
|
|
|
+ } else {
|
|
|
|
+ return 0;
|
|
|
|
+ }
|
|
|
|
+
|
|
|
|
+ return sony_check_add_dev_list(sc);
|
|
|
|
+}
|
|
|
|
+
|
|
|
|
+
|
|
static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|
static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|
{
|
|
{
|
|
int ret;
|
|
int ret;
|
|
@@ -1556,6 +1689,10 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
|
|
ret = 0;
|
|
ret = 0;
|
|
}
|
|
}
|
|
|
|
|
|
|
|
+ if (ret < 0)
|
|
|
|
+ goto err_stop;
|
|
|
|
+
|
|
|
|
+ ret = sony_check_add(sc);
|
|
if (ret < 0)
|
|
if (ret < 0)
|
|
goto err_stop;
|
|
goto err_stop;
|
|
|
|
|
|
@@ -1594,6 +1731,7 @@ err_stop:
|
|
sony_battery_remove(sc);
|
|
sony_battery_remove(sc);
|
|
if (sc->worker_initialized)
|
|
if (sc->worker_initialized)
|
|
cancel_work_sync(&sc->state_worker);
|
|
cancel_work_sync(&sc->state_worker);
|
|
|
|
+ sony_remove_dev_list(sc);
|
|
hid_hw_stop(hdev);
|
|
hid_hw_stop(hdev);
|
|
return ret;
|
|
return ret;
|
|
}
|
|
}
|
|
@@ -1613,6 +1751,8 @@ static void sony_remove(struct hid_device *hdev)
|
|
if (sc->worker_initialized)
|
|
if (sc->worker_initialized)
|
|
cancel_work_sync(&sc->state_worker);
|
|
cancel_work_sync(&sc->state_worker);
|
|
|
|
|
|
|
|
+ sony_remove_dev_list(sc);
|
|
|
|
+
|
|
hid_hw_stop(hdev);
|
|
hid_hw_stop(hdev);
|
|
}
|
|
}
|
|
|
|
|