aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/hid/hid-sony.c
diff options
context:
space:
mode:
authorFrank Praznik <frank.praznik@oh.rr.com>2014-02-20 11:36:03 -0500
committerJiri Kosina <jkosina@suse.cz>2014-02-24 11:38:46 -0500
commitd2d782fccee4f699a35e2d0cdbb2b19bdaec95a4 (patch)
treeea947847d645a59d102f487b04fe5e63d209d436 /drivers/hid/hid-sony.c
parentac3c9a94094b515ab135886eb4547bb889d5b31a (diff)
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 <frank.praznik@oh.rr.com> Reviewed-by: David Herrmann <dh.herrmann@gmail.com> Signed-off-by: Jiri Kosina <jkosina@suse.cz>
Diffstat (limited to 'drivers/hid/hid-sony.c')
-rw-r--r--drivers/hid/hid-sony.c140
1 files changed, 140 insertions, 0 deletions
diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
index b39e3abd6cdd..b1aa6f00c827 100644
--- a/drivers/hid/hid-sony.c
+++ b/drivers/hid/hid-sony.c
@@ -33,6 +33,7 @@
33#include <linux/leds.h> 33#include <linux/leds.h>
34#include <linux/power_supply.h> 34#include <linux/power_supply.h>
35#include <linux/spinlock.h> 35#include <linux/spinlock.h>
36#include <linux/list.h>
36#include <linux/input/mt.h> 37#include <linux/input/mt.h>
37 38
38#include "hid-ids.h" 39#include "hid-ids.h"
@@ -717,8 +718,12 @@ static enum power_supply_property sony_battery_props[] = {
717 POWER_SUPPLY_PROP_STATUS, 718 POWER_SUPPLY_PROP_STATUS,
718}; 719};
719 720
721static spinlock_t sony_dev_list_lock;
722static LIST_HEAD(sony_device_list);
723
720struct sony_sc { 724struct sony_sc {
721 spinlock_t lock; 725 spinlock_t lock;
726 struct list_head list_node;
722 struct hid_device *hdev; 727 struct hid_device *hdev;
723 struct led_classdev *leds[MAX_LEDS]; 728 struct led_classdev *leds[MAX_LEDS];
724 unsigned long quirks; 729 unsigned long quirks;
@@ -730,6 +735,7 @@ struct sony_sc {
730 __u8 right; 735 __u8 right;
731#endif 736#endif
732 737
738 __u8 mac_address[6];
733 __u8 worker_initialized; 739 __u8 worker_initialized;
734 __u8 cable_state; 740 __u8 cable_state;
735 __u8 battery_charging; 741 __u8 battery_charging;
@@ -1489,6 +1495,133 @@ static int sony_register_touchpad(struct sony_sc *sc, int touch_count,
1489 return 0; 1495 return 0;
1490} 1496}
1491 1497
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 */
1504static 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
1526unlock:
1527 spin_unlock_irqrestore(&sony_dev_list_lock, flags);
1528 return ret;
1529}
1530
1531static 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
1542static 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
1562static 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
1492static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id) 1625static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
1493{ 1626{
1494 int ret; 1627 int ret;
@@ -1559,6 +1692,10 @@ static int sony_probe(struct hid_device *hdev, const struct hid_device_id *id)
1559 if (ret < 0) 1692 if (ret < 0)
1560 goto err_stop; 1693 goto err_stop;
1561 1694
1695 ret = sony_check_add(sc);
1696 if (ret < 0)
1697 goto err_stop;
1698
1562 if (sc->quirks & SONY_LED_SUPPORT) { 1699 if (sc->quirks & SONY_LED_SUPPORT) {
1563 ret = sony_leds_init(hdev); 1700 ret = sony_leds_init(hdev);
1564 if (ret < 0) 1701 if (ret < 0)
@@ -1594,6 +1731,7 @@ err_stop:
1594 sony_battery_remove(sc); 1731 sony_battery_remove(sc);
1595 if (sc->worker_initialized) 1732 if (sc->worker_initialized)
1596 cancel_work_sync(&sc->state_worker); 1733 cancel_work_sync(&sc->state_worker);
1734 sony_remove_dev_list(sc);
1597 hid_hw_stop(hdev); 1735 hid_hw_stop(hdev);
1598 return ret; 1736 return ret;
1599} 1737}
@@ -1613,6 +1751,8 @@ static void sony_remove(struct hid_device *hdev)
1613 if (sc->worker_initialized) 1751 if (sc->worker_initialized)
1614 cancel_work_sync(&sc->state_worker); 1752 cancel_work_sync(&sc->state_worker);
1615 1753
1754 sony_remove_dev_list(sc);
1755
1616 hid_hw_stop(hdev); 1756 hid_hw_stop(hdev);
1617} 1757}
1618 1758