From patchwork Fri Feb 3 00:21:06 2017 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roderick Colenbrander X-Patchwork-Id: 9553387 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id E2FBD60453 for ; Fri, 3 Feb 2017 00:21:39 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D31DB205F6 for ; Fri, 3 Feb 2017 00:21:39 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id C7E61262F0; Fri, 3 Feb 2017 00:21:39 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.3 required=2.0 tests=BAYES_00,DKIM_SIGNED, RCVD_IN_DNSWL_HI, RCVD_IN_SORBS_SPAM, T_DKIM_INVALID autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 406D3205F6 for ; Fri, 3 Feb 2017 00:21:39 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1751944AbdBCAVi (ORCPT ); Thu, 2 Feb 2017 19:21:38 -0500 Received: from mail-pf0-f178.google.com ([209.85.192.178]:32844 "EHLO mail-pf0-f178.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1751565AbdBCAVh (ORCPT ); Thu, 2 Feb 2017 19:21:37 -0500 Received: by mail-pf0-f178.google.com with SMTP id y143so1112675pfb.0 for ; Thu, 02 Feb 2017 16:21:37 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gaikai-com.20150623.gappssmtp.com; s=20150623; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=fVNhPYv4nICxL9zZrokjzEqAtMYfvcbNEmXj3ilcz4o=; b=UMz6onFTA2wkt6HLuJxXKCzaKeSuOaVViQlckMGUzZ/ReYN55fAErucvGR+xBXkUyw hmr4Iw5E4LsFK2uryDeCMbwf8WLJLygioCFOIJ216DCeBH5ENiuLmhfbcQoE/4gyaldW QdNG95NMyZqGfrlVo+mw9kwmBP+hnmvTyDE67V/Ts/HnGffi2/Io58tuGbm05dFi6iNj dX/x9uDVgPWAnW8LoRc5KsOC89II13b4kiaLEh8tsPBy1NV5n8aUbROlArUyKmUqcpWu hYkbD6sTjF23CbDsVHFE7ijmwzXbTup588RkdJeQ0dRyRySrk+nYeKXfcpo1ZWhm5JO3 CIjg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=fVNhPYv4nICxL9zZrokjzEqAtMYfvcbNEmXj3ilcz4o=; b=VtNwDztXAnheGzSJpmJJSsVQvg6LJ19wUpexmd4YkL7zd9LxgJkk5q8jYT0ZHn4fV5 YJDj3czt+9bjTwIb4rgzw0F0LOXt4pDg022sJ2Hd/4SOaFQa15/dI1O5yPDyEj+fmJeT 8bMkLJCGVRq6vfOY9PIyrpDtH4AADwMid8dE3Dvd/hKSQWhLjOeN8C+L4dKk9C/DFZDM 95+vishdDcnVHpJAR4PrTsDJdyUl+4I4nm8XgI2zrtPWrzJX7VLrIL21f7Ai2r5766M/ QSisZv+tZ59oeCmpnXxq/mLi8xMiQwCPepGTeco6H2xMG/3Te/iogXVoPBOQItVXyRMI QDCg== X-Gm-Message-State: AIkVDXIA1Lqg7zwtzxqa3KEEoXG9ji7oXSzqIHwW7uzmuB1z65N7iYePGg2Z4W24xkkeJaMf X-Received: by 10.98.67.72 with SMTP id q69mr14182011pfa.14.1486081296469; Thu, 02 Feb 2017 16:21:36 -0800 (PST) Received: from konan1.dev.biz ([100.42.98.197]) by smtp.gmail.com with ESMTPSA id x81sm61343282pff.69.2017.02.02.16.21.35 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Thu, 02 Feb 2017 16:21:35 -0800 (PST) From: Roderick Colenbrander To: linux-input@vger.kernel.org Cc: Dmitry Torokhov , Jiri Kosina , Benjamin Tissoires , Simon Wood , Frank Praznik , Tim Bird , Roderick Colenbrander Subject: [PATCH 7/7] HID: sony: Support motion sensor calibration on dongle Date: Thu, 2 Feb 2017 16:21:06 -0800 Message-Id: <20170203002106.23225-8-roderick@gaikai.com> X-Mailer: git-send-email 2.9.3 In-Reply-To: <20170203002106.23225-1-roderick@gaikai.com> References: <20170203002106.23225-1-roderick@gaikai.com> Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP From: Roderick Colenbrander The DualShock 4 dongle isn't connected to a real DualShock 4 at time of driver loading. When a DualShock 4 is plugged in, we need to obtain calibration data (the dongle would have zeros). This patch adds calibration logic, which we schedule on a hotplug from sony_raw_event. In addition this patch adds dongle state handling. Signed-off-by: Roderick Colenbrander --- drivers/hid/hid-sony.c | 96 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 89 insertions(+), 7 deletions(-) diff --git a/drivers/hid/hid-sony.c b/drivers/hid/hid-sony.c index 11e32eb..34bdee2 100644 --- a/drivers/hid/hid-sony.c +++ b/drivers/hid/hid-sony.c @@ -624,8 +624,16 @@ struct ds4_calibration_data { int sens_denom; }; +enum ds4_dongle_state { + DONGLE_DISCONNECTED, + DONGLE_CALIBRATING, + DONGLE_CONNECTED, + DONGLE_DISABLED +}; + enum sony_worker { - SONY_WORKER_STATE + SONY_WORKER_STATE, + SONY_WORKER_HOTPLUG }; struct sony_sc { @@ -636,6 +644,7 @@ struct sony_sc { struct input_dev *sensor_dev; struct led_classdev *leds[MAX_LEDS]; unsigned long quirks; + struct work_struct hotplug_worker; struct work_struct state_worker; void (*send_output_report)(struct sony_sc *); struct power_supply *battery; @@ -649,6 +658,7 @@ struct sony_sc { #endif u8 mac_address[6]; + u8 hotplug_worker_initialized; u8 state_worker_initialized; u8 defer_initialization; u8 cable_state; @@ -664,7 +674,7 @@ struct sony_sc { u16 prev_timestamp; unsigned int timestamp_us; - bool ds4_dongle_connected; + enum ds4_dongle_state ds4_dongle_state; /* DS4 calibration data */ struct ds4_calibration_data ds4_calib_data[6]; }; @@ -678,6 +688,11 @@ static inline void sony_schedule_work(struct sony_sc *sc, case SONY_WORKER_STATE: if (!sc->defer_initialization) schedule_work(&sc->state_worker); + break; + case SONY_WORKER_HOTPLUG: + if (sc->hotplug_worker_initialized) + schedule_work(&sc->hotplug_worker); + break; } } @@ -1086,6 +1101,9 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report, dualshock4_parse_report(sc, rd, size); } else if ((sc->quirks & DUALSHOCK4_DONGLE) && rd[0] == 0x01 && size == 64) { + unsigned long flags; + enum ds4_dongle_state dongle_state; + /* * In the case of a DS4 USB dongle, bit[2] of byte 31 indicates * if a DS4 is actually connected (indicated by '0'). @@ -1093,16 +1111,45 @@ static int sony_raw_event(struct hid_device *hdev, struct hid_report *report, */ bool connected = (rd[31] & 0x04) ? false : true; - if (!sc->ds4_dongle_connected && connected) { + spin_lock_irqsave(&sc->lock, flags); + dongle_state = sc->ds4_dongle_state; + spin_unlock_irqrestore(&sc->lock, flags); + + /* + * The dongle always sends input reports even when no + * DS4 is attached. When a DS4 is connected, we need to + * obtain calibration data before we can use it. + * The code below tracks dongle state and kicks of + * calibration when needed and only allows us to process + * input if a DS4 is actually connected. + */ + if (dongle_state == DONGLE_DISCONNECTED && connected) { hid_info(sc->hdev, "DualShock 4 USB dongle: controller connected\n"); sony_set_leds(sc); - sc->ds4_dongle_connected = true; - } else if (sc->ds4_dongle_connected && !connected) { + + spin_lock_irqsave(&sc->lock, flags); + sc->ds4_dongle_state = DONGLE_CALIBRATING; + spin_unlock_irqrestore(&sc->lock, flags); + + sony_schedule_work(sc, SONY_WORKER_HOTPLUG); + + /* Don't process the report since we don't have + * calibration data, but let hidraw have it anyway. + */ + return 0; + } else if ((dongle_state == DONGLE_CONNECTED || + dongle_state == DONGLE_DISABLED) && !connected) { hid_info(sc->hdev, "DualShock 4 USB dongle: controller disconnected\n"); - sc->ds4_dongle_connected = false; + + spin_lock_irqsave(&sc->lock, flags); + sc->ds4_dongle_state = DONGLE_DISCONNECTED; + spin_unlock_irqrestore(&sc->lock, flags); + /* Return 0, so hidraw can get the report. */ return 0; - } else if (!sc->ds4_dongle_connected) { + } else if (dongle_state == DONGLE_CALIBRATING || + dongle_state == DONGLE_DISABLED || + dongle_state == DONGLE_DISCONNECTED) { /* Return 0, so hidraw can get the report. */ return 0; } @@ -1519,6 +1566,33 @@ static int dualshock4_get_calibration_data(struct sony_sc *sc) return ret; } +static void dualshock4_calibration_work(struct work_struct *work) +{ + struct sony_sc *sc = container_of(work, struct sony_sc, hotplug_worker); + unsigned long flags; + enum ds4_dongle_state dongle_state; + int ret; + + ret = dualshock4_get_calibration_data(sc); + if (ret < 0) { + /* This call is very unlikely to fail for the dongle. When it + * fails we are probably in a very bad state, so mark the + * dongle as disabled. We will re-enable the dongle if a new + * DS4 hotplug is detect from sony_raw_event as any issues + * are likely resolved then (the dongle is quite stupid). + */ + hid_err(sc->hdev, "DualShock 4 USB dongle: calibration failed, disabling device\n"); + dongle_state = DONGLE_DISABLED; + } else { + hid_info(sc->hdev, "DualShock 4 USB dongle: calibration completed\n"); + dongle_state = DONGLE_CONNECTED; + } + + spin_lock_irqsave(&sc->lock, flags); + sc->ds4_dongle_state = dongle_state; + spin_unlock_irqrestore(&sc->lock, flags); +} + static void sixaxis_set_leds_from_id(struct sony_sc *sc) { static const u8 sixaxis_leds[10][4] = { @@ -2362,6 +2436,8 @@ static inline void sony_init_output_report(struct sony_sc *sc, static inline void sony_cancel_work_sync(struct sony_sc *sc) { + if (sc->hotplug_worker_initialized) + cancel_work_sync(&sc->hotplug_worker); if (sc->state_worker_initialized) cancel_work_sync(&sc->state_worker); } @@ -2443,6 +2519,12 @@ static int sony_input_configured(struct hid_device *hdev, return ret; } + if (sc->quirks & DUALSHOCK4_DONGLE) { + INIT_WORK(&sc->hotplug_worker, dualshock4_calibration_work); + sc->hotplug_worker_initialized = 1; + sc->ds4_dongle_state = DONGLE_DISCONNECTED; + } + sony_init_output_report(sc, dualshock4_send_output_report); } else if (sc->quirks & MOTION_CONTROLLER) { sony_init_output_report(sc, motion_send_output_report);