Hi
On Wed, Jan 29, 2014 at 6:33 PM, Frank Praznik <[email protected]> wrote:
> Prevent one controller from being connected twice and showing up as two
> devices
> if a USB cable is plugged into the controller when it is already connected via
> Bluetooth.
>
> A global list of connected devices is maintained and newly connected
> controllers
> are checked against this list. If it is found to already be present, the probe
> function exits with an return value of EEXIST.
>
> The MAC address of the Dualshock 4 is used as an identifier to track connected
> controllers. It is retrieved with feature report 0x81 when connected via USB
> and
> via the UNIQ identifier on a Bluetooth connection.
>
> Signed-off-by: Frank Praznik <[email protected]>
>
> ---
> drivers/hid/hid-sony.c | 107
> ++++++++++++++++++++++++++++++++++++++++++++++++-
> 1 file changed, 105 insertions(+), 2 deletions(-)
>
> diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c
> index e478265..a24d021 100644
> --- a/drivers/hid/hid-sony.c
> +++ b/drivers/hid/hid-sony.c
> @@ -710,8 +710,12 @@ static enum power_supply_property sony_battery_props[] =
> {
> POWER_SUPPLY_PROP_STATUS,
> };
>
> +static spinlock_t sony_dev_list_lock;
> +static LIST_HEAD(sony_device_list);
> +
> struct sony_sc {
> spinlock_t lock;
> + struct list_head device_list;
> struct hid_device *hdev;
> struct led_classdev *leds[MAX_LEDS];
> unsigned long quirks;
> @@ -723,6 +727,7 @@ struct sony_sc {
> __u8 right;
> #endif
>
> + __u8 mac_address[6];
> __u8 cable_state;
> __u8 battery_charging;
> __u8 battery_capacity;
> @@ -1471,6 +1476,94 @@ static int sony_register_touchpad(struct sony_sc *sc,
> int touch_count,
> 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;
> + struct list_head *pos;
> + unsigned long flags;
> + int ret;
> +
> + spin_lock_irqsave(&sony_dev_list_lock, flags);
> +
> + list_for_each(pos, &sony_device_list) {
> + entry = list_entry(pos, struct sony_sc, device_list);
> + ret = memcmp(sc->mac_address, entry->mac_address,
> + FIELD_SIZEOF(struct sony_sc, mac_address));
> + if (!ret) {
> + hid_info(sc->hdev, "Controller already connected\n");
> + return -EEXIST;
> + }
> + }
> +
> + list_add(&(sc->device_list), &sony_device_list);
> +
> + spin_unlock_irqrestore(&sony_dev_list_lock, flags);
> +
> + return 0;
> +}
> +
> +static void sony_remove_dev_list(struct sony_sc *sc)
> +{
> + unsigned long flags;
> +
> + spin_lock_irqsave(&sony_dev_list_lock, flags);
> + list_del(&(sc->device_list));
> + spin_unlock_irqrestore(&sony_dev_list_lock, flags);
> +}
> +
> +static int dualshock4_check_add(struct sony_sc *sc)
> +{
> + int ret;
> +
> + if (sc->quirks & DUALSHOCK4_CONTROLLER_BT) {
> + int n;
> + unsigned int mac_addr[6];
> +
> + /* HIDP stores the device MAC address in the uniq member */
> + ret = strlen(sc->hdev->uniq);
> + if (ret != 17) {
> + hid_err(sc->hdev, "Malformed controller MAC
> address\n");
> + return -EINVAL;
> + }
> +
> + ret = sscanf(sc->hdev->uniq, "%02x:%02x:%02x:%02x:%02x:%02x",
> + &mac_addr[5], &mac_addr[4], &mac_addr[3],
> &mac_addr[2],
> + &mac_addr[1], &mac_addr[0]);
Don't do this. UNIQ is a way to provide unique identifiers for
devices, but it's not guaranteed to stay the same.
Is it not possible to use the same FEATURE_REPORT as with USB?
Thanks
David
> +
> + if (ret != 6) {
> + hid_err(sc->hdev, "Error parsing controller MAC
> address\n");
> + return -EINVAL;
> + }
> +
> + for (n = 5; n >= 0; n--)
> + sc->mac_address[n] = (__u8)mac_addr[n];
> + } else {
> + __u8 buf[7];
> +
> + /* The MAC address of a DS4 controller connected via USB can
> be
> + * retrieved with feature report 0x81.
> + */
> + ret = sc->hdev->ll_driver->raw_request(sc->hdev, 0x81,
> + buf, sizeof(buf), HID_FEATURE_REPORT,
> + HID_REQ_GET_REPORT);
> +
> + if (ret != 7) {
> + hid_err(sc->hdev, "Error retrieving with controller
> MAC address\n");
> + return ret;
> + }
> +
> + memcpy(sc->mac_address, &buf[1], sizeof(sc->mac_address));
> + }
> +
> + ret = sony_check_add_dev_list(sc);
> + return ret;
> +}
> +
> static int sony_probe(struct hid_device *hdev, const struct hid_device_id
> *id)
> {
> int ret;
> @@ -1515,8 +1608,13 @@ static int sony_probe(struct hid_device *hdev, const
> struct hid_device_id *id)
> else if (sc->quirks & SIXAXIS_CONTROLLER_BT)
> ret = sixaxis_set_operational_bt(hdev);
> else if ((sc->quirks & DUALSHOCK4_CONTROLLER_USB) ||
> - (sc->quirks & DUALSHOCK4_CONTROLLER_BT)) {
> - /* The Dualshock 4 touchpad supports 2 touches and has a
> + (sc->quirks & DUALSHOCK4_CONTROLLER_BT)) {
> +
> + ret = dualshock4_check_add(sc);
> + if (ret)
> + goto err_stop;
> +
> + /* The Dualshock 4 touchpad supports 2 touches and has a
> * resolution of 1920x940.
> */
> ret = sony_register_touchpad(sc, 2, 1920, 940);
> @@ -1580,6 +1678,11 @@ static void sony_remove(struct hid_device *hdev)
>
> sony_destroy_ff(hdev);
>
> + if ((sc->quirks & DUALSHOCK4_CONTROLLER_USB) ||
> + (sc->quirks & DUALSHOCK4_CONTROLLER_BT)) {
> + sony_remove_dev_list(sc);
> + }
> +
> hid_hw_stop(hdev);
> }
>
> --
> 1.8.5.3
>
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to [email protected]
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
To unsubscribe from this list: send the line "unsubscribe linux-input" in
the body of a message to [email protected]
More majordomo info at http://vger.kernel.org/majordomo-info.html