Skip to content

Commit d2d782f

Browse files
Frank PraznikJiri Kosina
authored andcommitted
HID: sony: Prevent duplicate controller connections.
If a Sixaxis or Dualshock 4 controller is connected via USB while already connected via Bluetooth it will cause duplicate devices to be added to the input device list. To prevent this a global list of controllers and their MAC addresses is maintained and new controllers are checked against this list. If a duplicate is found, the probe function will exit with -EEXIST. On USB the MAC is retrieved via a feature report. On Bluetooth neither controller reports the MAC address in a feature report so the MAC is parsed from the uniq string. As uniq cannot be guaranteed to be a MAC address in every case (uHID or the behavior of HIDP changing) a parsing failure will not prevent the connection. Signed-off-by: Frank Praznik <[email protected]> Reviewed-by: David Herrmann <[email protected]> Signed-off-by: Jiri Kosina <[email protected]>
1 parent ac3c9a9 commit d2d782f

File tree

1 file changed

+140
-0
lines changed

1 file changed

+140
-0
lines changed

drivers/hid/hid-sony.c

Lines changed: 140 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@
3333
#include <linux/leds.h>
3434
#include <linux/power_supply.h>
3535
#include <linux/spinlock.h>
36+
#include <linux/list.h>
3637
#include <linux/input/mt.h>
3738

3839
#include "hid-ids.h"
@@ -717,8 +718,12 @@ static enum power_supply_property sony_battery_props[] = {
717718
POWER_SUPPLY_PROP_STATUS,
718719
};
719720

721+
static spinlock_t sony_dev_list_lock;
722+
static LIST_HEAD(sony_device_list);
723+
720724
struct sony_sc {
721725
spinlock_t lock;
726+
struct list_head list_node;
722727
struct hid_device *hdev;
723728
struct led_classdev *leds[MAX_LEDS];
724729
unsigned long quirks;
@@ -730,6 +735,7 @@ struct sony_sc {
730735
__u8 right;
731736
#endif
732737

738+
__u8 mac_address[6];
733739
__u8 worker_initialized;
734740
__u8 cable_state;
735741
__u8 battery_charging;
@@ -1489,6 +1495,133 @@ static int sony_register_touchpad(struct sony_sc *sc, int touch_count,
14891495
return 0;
14901496
}
14911497

1498+
/*
1499+
* If a controller is plugged in via USB while already connected via Bluetooth
1500+
* it will show up as two devices. A global list of connected controllers and
1501+
* their MAC addresses is maintained to ensure that a device is only connected
1502+
* once.
1503+
*/
1504+
static int sony_check_add_dev_list(struct sony_sc *sc)
1505+
{
1506+
struct sony_sc *entry;
1507+
unsigned long flags;
1508+
int ret;
1509+
1510+
spin_lock_irqsave(&sony_dev_list_lock, flags);
1511+
1512+
list_for_each_entry(entry, &sony_device_list, list_node) {
1513+
ret = memcmp(sc->mac_address, entry->mac_address,
1514+
sizeof(sc->mac_address));
1515+
if (!ret) {
1516+
ret = -EEXIST;
1517+
hid_info(sc->hdev, "controller with MAC address %pMR already connected\n",
1518+
sc->mac_address);
1519+
goto unlock;
1520+
}
1521+
}
1522+
1523+
ret = 0;
1524+
list_add(&(sc->list_node), &sony_device_list);
1525+
1526+
unlock:
1527+
spin_unlock_irqrestore(&sony_dev_list_lock, flags);
1528+
return ret;
1529+
}
1530+
1531+
static void sony_remove_dev_list(struct sony_sc *sc)
1532+
{
1533+
unsigned long flags;
1534+
1535+
if (sc->list_node.next) {
1536+
spin_lock_irqsave(&sony_dev_list_lock, flags);
1537+
list_del(&(sc->list_node));
1538+
spin_unlock_irqrestore(&sony_dev_list_lock, flags);
1539+
}
1540+
}
1541+
1542+
static int sony_get_bt_devaddr(struct sony_sc *sc)
1543+
{
1544+
int ret;
1545+
1546+
/* HIDP stores the device MAC address as a string in the uniq field. */
1547+
ret = strlen(sc->hdev->uniq);
1548+
if (ret != 17)
1549+
return -EINVAL;
1550+
1551+
ret = sscanf(sc->hdev->uniq,
1552+
"%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx",
1553+
&sc->mac_address[5], &sc->mac_address[4], &sc->mac_address[3],
1554+
&sc->mac_address[2], &sc->mac_address[1], &sc->mac_address[0]);
1555+
1556+
if (ret != 6)
1557+
return -EINVAL;
1558+
1559+
return 0;
1560+
}
1561+
1562+
static int sony_check_add(struct sony_sc *sc)
1563+
{
1564+
int n, ret;
1565+
1566+
if ((sc->quirks & DUALSHOCK4_CONTROLLER_BT) ||
1567+
(sc->quirks & SIXAXIS_CONTROLLER_BT)) {
1568+
/*
1569+
* sony_get_bt_devaddr() attempts to parse the Bluetooth MAC
1570+
* address from the uniq string where HIDP stores it.
1571+
* As uniq cannot be guaranteed to be a MAC address in all cases
1572+
* a failure of this function should not prevent the connection.
1573+
*/
1574+
if (sony_get_bt_devaddr(sc) < 0) {
1575+
hid_warn(sc->hdev, "UNIQ does not contain a MAC address; duplicate check skipped\n");
1576+
return 0;
1577+
}
1578+
} else if (sc->quirks & DUALSHOCK4_CONTROLLER_USB) {
1579+
__u8 buf[7];
1580+
1581+
/*
1582+
* The MAC address of a DS4 controller connected via USB can be
1583+
* retrieved with feature report 0x81. The address begins at
1584+
* offset 1.
1585+
*/
1586+
ret = hid_hw_raw_request(sc->hdev, 0x81, buf, sizeof(buf),
1587+
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
1588+
1589+
if (ret != 7) {
1590+
hid_err(sc->hdev, "failed to retrieve feature report 0x81 with the DualShock 4 MAC address\n");
1591+
return ret < 0 ? ret : -EINVAL;
1592+
}
1593+
1594+
memcpy(sc->mac_address, &buf[1], sizeof(sc->mac_address));
1595+
} else if (sc->quirks & SIXAXIS_CONTROLLER_USB) {
1596+
__u8 buf[18];
1597+
1598+
/*
1599+
* The MAC address of a Sixaxis controller connected via USB can
1600+
* be retrieved with feature report 0xf2. The address begins at
1601+
* offset 4.
1602+
*/
1603+
ret = hid_hw_raw_request(sc->hdev, 0xf2, buf, sizeof(buf),
1604+
HID_FEATURE_REPORT, HID_REQ_GET_REPORT);
1605+
1606+
if (ret != 18) {
1607+
hid_err(sc->hdev, "failed to retrieve feature report 0xf2 with the Sixaxis MAC address\n");
1608+
return ret < 0 ? ret : -EINVAL;
1609+
}
1610+
1611+
/*
1612+
* The Sixaxis device MAC in the report is big-endian and must
1613+
* be byte-swapped.
1614+
*/
1615+
for (n = 0; n < 6; n++)
1616+
sc->mac_address[5-n] = buf[4+n];
1617+
} else {
1618+
return 0;
1619+
}
1620+
1621+
return sony_check_add_dev_list(sc);
1622+
}
1623+
1624+
14921625
static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
14931626
{
14941627
int ret;
@@ -1556,6 +1689,10 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
15561689
ret = 0;
15571690
}
15581691

1692+
if (ret < 0)
1693+
goto err_stop;
1694+
1695+
ret = sony_check_add(sc);
15591696
if (ret < 0)
15601697
goto err_stop;
15611698

@@ -1594,6 +1731,7 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
15941731
sony_battery_remove(sc);
15951732
if (sc->worker_initialized)
15961733
cancel_work_sync(&sc->state_worker);
1734+
sony_remove_dev_list(sc);
15971735
hid_hw_stop(hdev);
15981736
return ret;
15991737
}
@@ -1613,6 +1751,8 @@ static void sony_remove(struct hid_device *hdev)
16131751
if (sc->worker_initialized)
16141752
cancel_work_sync(&sc->state_worker);
16151753

1754+
sony_remove_dev_list(sc);
1755+
16161756
hid_hw_stop(hdev);
16171757
}
16181758

0 commit comments

Comments
 (0)