From patchwork Sat Oct 29 18:48:39 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roderick Colenbrander X-Patchwork-Id: 619935 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 27A5CC38A02 for ; Sat, 29 Oct 2022 18:49:23 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229562AbiJ2StV (ORCPT ); Sat, 29 Oct 2022 14:49:21 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47210 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229629AbiJ2StU (ORCPT ); Sat, 29 Oct 2022 14:49:20 -0400 Received: from mail-pl1-x62b.google.com (mail-pl1-x62b.google.com [IPv6:2607:f8b0:4864:20::62b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1FC244599C for ; Sat, 29 Oct 2022 11:49:19 -0700 (PDT) Received: by mail-pl1-x62b.google.com with SMTP id v17so4513655plo.1 for ; Sat, 29 Oct 2022 11:49:19 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gaikai-com.20210112.gappssmtp.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=TUwtoqb6ymCe0xoGoi2RycTEiGOOXs1xPWthLoNdQkg=; b=yNjpTHL5jlDHYjA1hdbf0MHt/JoEgSYKr2TwBWsY1mNKk48xl05HTZhPRsQZ3/8LVY UJaoWfDNsAO4KQ+MJ7GXxu4lTEgs3WzxBZDAOpgAlduHydi/Du06/nOmKqacyTODtrNW JW4MX76y3UteJ6Zf1qECb0HS9MpP6VJlILxbBMsjbuCyCqCqq87QDmTb77l4bFsQ+wN5 LibabHzOk/le2K7E0llBCWH90qN7mkK/CgvPFikx0c0a1h32ynENheJ2qO4I53lVqgGf K25NsDuzRKjtj2s0aSrc1GIigVOYciPrd5khnB8HZHE9NIL5I6zC3pPLAcUK8lBoLyHX pCTw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=TUwtoqb6ymCe0xoGoi2RycTEiGOOXs1xPWthLoNdQkg=; b=YkorCtsIC2rN3gyy8fUJ41B1IYzNE1pPCGjeNJfXGvi6SSsT59y+vpwJsattp1DTC7 j5OM4xvNoKoLtlJMTBlIuA4cdpFRafNBJhUEoxn5HuVyvCXyZMBfrxgetBY8FjyQ+SPN Hpl7B7besbuJ+RbZd6xx8CS+3530q+r0vDz1GjlEXAHFURkYHFf+ZYTeSAKPvbXZQvN9 J04qTYbEUYaBJi7oj6oz9Qx/+V5KrqC0Gs3HfJYZYJ2jTU7Lgrg4mapXAdcvdKDzTNBs cFvnpfb/47omLq0j1rcewU0vXUIXqNDAfOVg1KOVHTM8REYXMb4d38NATpCEUlIrZox3 NzsA== X-Gm-Message-State: ACrzQf3bgdQQcD3ds7HIwgpUvYeBOtwDZvHUeGLP7ywDsyTJZjEW++7x Q/ra9GA26Lcu0DydCGKjsun2sA== X-Google-Smtp-Source: AMsMyM7Pp/4+L+f18KyHbNjDeEvvazUEEaioxvGKmtoVjx3ZmrxUCf9hzkttKXgExOayCDOiYYyTtg== X-Received: by 2002:a17:902:d2c9:b0:186:f355:a42b with SMTP id n9-20020a170902d2c900b00186f355a42bmr5575302plc.58.1667069353771; Sat, 29 Oct 2022 11:49:13 -0700 (PDT) Received: from localhost.localdomain ([76.242.116.65]) by smtp.gmail.com with ESMTPSA id gb7-20020a17090b060700b0020d9306e735sm1377566pjb.20.2022.10.29.11.49.08 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 29 Oct 2022 11:49:13 -0700 (PDT) From: Roderick Colenbrander X-Google-Original-From: Roderick Colenbrander To: Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, Roderick Colenbrander Subject: [PATCH 01/13] HID: playstation: initial DualShock4 USB support. Date: Sat, 29 Oct 2022 11:48:39 -0700 Message-Id: <20221029184851.282366-2-roderick.colenbrander@sony.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221029184851.282366-1-roderick.colenbrander@sony.com> References: <20221029184851.282366-1-roderick.colenbrander@sony.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org Add basic support for DualShock4 USB controller with buttons and sticks. Signed-off-by: Roderick Colenbrander --- drivers/hid/hid-playstation.c | 174 +++++++++++++++++++++++++++++++++- 1 file changed, 172 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c index 0b58763bfd30..04d03d2e40db 100644 --- a/drivers/hid/hid-playstation.c +++ b/drivers/hid/hid-playstation.c @@ -2,7 +2,7 @@ /* * HID driver for Sony DualSense(TM) controller. * - * Copyright (c) 2020 Sony Interactive Entertainment + * Copyright (c) 2020-2022 Sony Interactive Entertainment */ #include @@ -283,6 +283,35 @@ struct dualsense_output_report { struct dualsense_output_report_common *common; }; +#define DS4_INPUT_REPORT_USB 0x01 +#define DS4_INPUT_REPORT_USB_SIZE 64 + +#define DS4_FEATURE_REPORT_PAIRING_INFO 0x12 +#define DS4_FEATURE_REPORT_PAIRING_INFO_SIZE 16 + +struct dualshock4 { + struct ps_device base; + struct input_dev *gamepad; +}; + +/* Main DualShock4 input report excluding any BT/USB specific headers. */ +struct dualshock4_input_report_common { + uint8_t x, y; + uint8_t rx, ry; + uint8_t buttons[3]; + uint8_t z, rz; + + uint8_t reserved[20]; +} __packed; +static_assert(sizeof(struct dualshock4_input_report_common) == 29); + +struct dualshock4_input_report_usb { + uint8_t report_id; /* 0x01 */ + struct dualshock4_input_report_common common; + uint8_t reserved[34]; +} __packed; +static_assert(sizeof(struct dualshock4_input_report_usb) == DS4_INPUT_REPORT_USB_SIZE); + /* * Common gamepad buttons across DualShock 3 / 4 and DualSense. * Note: for device with a touchpad, touchpad button is not included @@ -1465,6 +1494,135 @@ static struct ps_device *dualsense_create(struct hid_device *hdev) return ERR_PTR(ret); } +static int dualshock4_get_mac_address(struct dualshock4 *ds4) +{ + uint8_t *buf; + int ret = 0; + + buf = kzalloc(DS4_FEATURE_REPORT_PAIRING_INFO_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = ps_get_report(ds4->base.hdev, DS4_FEATURE_REPORT_PAIRING_INFO, buf, + DS4_FEATURE_REPORT_PAIRING_INFO_SIZE); + if (ret) { + hid_err(ds4->base.hdev, "Failed to retrieve DualShock4 pairing info: %d\n", ret); + goto err_free; + } + + memcpy(ds4->base.mac_address, &buf[1], sizeof(ds4->base.mac_address)); + +err_free: + kfree(buf); + return ret; +} + +static int dualshock4_parse_report(struct ps_device *ps_dev, struct hid_report *report, + u8 *data, int size) +{ + struct hid_device *hdev = ps_dev->hdev; + struct dualshock4 *ds4 = container_of(ps_dev, struct dualshock4, base); + struct dualshock4_input_report_common *ds4_report; + uint8_t value; + + /* + * DualShock4 in USB uses the full HID report for reportID 1, but + * Bluetooth uses a minimal HID report for reportID 1 and reports + * the full report using reportID 17. + */ + if (hdev->bus == BUS_USB && report->id == DS4_INPUT_REPORT_USB && + size == DS4_INPUT_REPORT_USB_SIZE) { + struct dualshock4_input_report_usb *usb = (struct dualshock4_input_report_usb *)data; + + ds4_report = &usb->common; + } else { + hid_err(hdev, "Unhandled reportID=%d\n", report->id); + return -1; + } + + input_report_abs(ds4->gamepad, ABS_X, ds4_report->x); + input_report_abs(ds4->gamepad, ABS_Y, ds4_report->y); + input_report_abs(ds4->gamepad, ABS_RX, ds4_report->rx); + input_report_abs(ds4->gamepad, ABS_RY, ds4_report->ry); + input_report_abs(ds4->gamepad, ABS_Z, ds4_report->z); + input_report_abs(ds4->gamepad, ABS_RZ, ds4_report->rz); + + value = ds4_report->buttons[0] & DS_BUTTONS0_HAT_SWITCH; + if (value >= ARRAY_SIZE(ps_gamepad_hat_mapping)) + value = 8; /* center */ + input_report_abs(ds4->gamepad, ABS_HAT0X, ps_gamepad_hat_mapping[value].x); + input_report_abs(ds4->gamepad, ABS_HAT0Y, ps_gamepad_hat_mapping[value].y); + + input_report_key(ds4->gamepad, BTN_WEST, ds4_report->buttons[0] & DS_BUTTONS0_SQUARE); + input_report_key(ds4->gamepad, BTN_SOUTH, ds4_report->buttons[0] & DS_BUTTONS0_CROSS); + input_report_key(ds4->gamepad, BTN_EAST, ds4_report->buttons[0] & DS_BUTTONS0_CIRCLE); + input_report_key(ds4->gamepad, BTN_NORTH, ds4_report->buttons[0] & DS_BUTTONS0_TRIANGLE); + input_report_key(ds4->gamepad, BTN_TL, ds4_report->buttons[1] & DS_BUTTONS1_L1); + input_report_key(ds4->gamepad, BTN_TR, ds4_report->buttons[1] & DS_BUTTONS1_R1); + input_report_key(ds4->gamepad, BTN_TL2, ds4_report->buttons[1] & DS_BUTTONS1_L2); + input_report_key(ds4->gamepad, BTN_TR2, ds4_report->buttons[1] & DS_BUTTONS1_R2); + input_report_key(ds4->gamepad, BTN_SELECT, ds4_report->buttons[1] & DS_BUTTONS1_CREATE); + input_report_key(ds4->gamepad, BTN_START, ds4_report->buttons[1] & DS_BUTTONS1_OPTIONS); + input_report_key(ds4->gamepad, BTN_THUMBL, ds4_report->buttons[1] & DS_BUTTONS1_L3); + input_report_key(ds4->gamepad, BTN_THUMBR, ds4_report->buttons[1] & DS_BUTTONS1_R3); + input_report_key(ds4->gamepad, BTN_MODE, ds4_report->buttons[2] & DS_BUTTONS2_PS_HOME); + input_sync(ds4->gamepad); + + return 0; +} + +static struct ps_device *dualshock4_create(struct hid_device *hdev) +{ + struct dualshock4 *ds4; + struct ps_device *ps_dev; + int ret; + + ds4 = devm_kzalloc(&hdev->dev, sizeof(*ds4), GFP_KERNEL); + if (!ds4) + return ERR_PTR(-ENOMEM); + + /* + * Patch version to allow userspace to distinguish between + * hid-generic vs hid-playstation axis and button mapping. + */ + hdev->version |= HID_PLAYSTATION_VERSION_PATCH; + + ps_dev = &ds4->base; + ps_dev->hdev = hdev; + spin_lock_init(&ps_dev->lock); + ps_dev->parse_report = dualshock4_parse_report; + hid_set_drvdata(hdev, ds4); + + ret = dualshock4_get_mac_address(ds4); + if (ret) { + hid_err(hdev, "Failed to get MAC address from DualShock4\n"); + return ERR_PTR(ret); + } + snprintf(hdev->uniq, sizeof(hdev->uniq), "%pMR", ds4->base.mac_address); + + ret = ps_devices_list_add(ps_dev); + if (ret) + return ERR_PTR(ret); + + ds4->gamepad = ps_gamepad_create(hdev, NULL); + if (IS_ERR(ds4->gamepad)) { + ret = PTR_ERR(ds4->gamepad); + goto err; + } + + ret = ps_device_set_player_id(ps_dev); + if (ret) { + hid_err(hdev, "Failed to assign player id for DualShock4: %d\n", ret); + goto err; + } + + return &ds4->base; + +err: + ps_devices_list_remove(ps_dev); + return ERR_PTR(ret); +} + static int ps_raw_event(struct hid_device *hdev, struct hid_report *report, u8 *data, int size) { @@ -1499,7 +1657,15 @@ static int ps_probe(struct hid_device *hdev, const struct hid_device_id *id) goto err_stop; } - if (hdev->product == USB_DEVICE_ID_SONY_PS5_CONTROLLER || + if (hdev->product == USB_DEVICE_ID_SONY_PS4_CONTROLLER || + hdev->product == USB_DEVICE_ID_SONY_PS4_CONTROLLER_2) { + dev = dualshock4_create(hdev); + if (IS_ERR(dev)) { + hid_err(hdev, "Failed to create dualshock4.\n"); + ret = PTR_ERR(dev); + goto err_close; + } + } else if (hdev->product == USB_DEVICE_ID_SONY_PS5_CONTROLLER || hdev->product == USB_DEVICE_ID_SONY_PS5_CONTROLLER_2) { dev = dualsense_create(hdev); if (IS_ERR(dev)) { @@ -1533,6 +1699,10 @@ static void ps_remove(struct hid_device *hdev) } static const struct hid_device_id ps_devices[] = { + /* Sony DualShock 4 controllers for PS4 */ + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2) }, + /* Sony DualSense controllers for PS5 */ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS5_CONTROLLER) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS5_CONTROLLER) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS5_CONTROLLER_2) }, From patchwork Sat Oct 29 18:48:40 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roderick Colenbrander X-Patchwork-Id: 619936 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 455DEC433FE for ; Sat, 29 Oct 2022 18:49:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229500AbiJ2StR (ORCPT ); Sat, 29 Oct 2022 14:49:17 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47172 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229515AbiJ2StQ (ORCPT ); Sat, 29 Oct 2022 14:49:16 -0400 Received: from mail-pf1-x435.google.com (mail-pf1-x435.google.com [IPv6:2607:f8b0:4864:20::435]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id F07EB4599B for ; Sat, 29 Oct 2022 11:49:15 -0700 (PDT) Received: by mail-pf1-x435.google.com with SMTP id e4so7440856pfl.2 for ; Sat, 29 Oct 2022 11:49:15 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gaikai-com.20210112.gappssmtp.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=pYhAzsJ2Du85shITNB+9upl/fTKX2vmS2iYh656fq+w=; b=M8uXA/N/tAIRUz0h1l+x2W0EAy4NU1aCaRMPr3kVCD0bQOsgsBZyPQtzD7ul6CIaql T+HATxSH19VNDPTkmaXkO3cuSrvTCp34mZ+A1y1h7rtosJCpr07NbW9Z3qNg7LiHkLX4 bnHVPlIMyFtirLkfmLQeDRMVLhuLlXaYe/iVeS8yJYn5DeCXK/tv6XNZjYtNyNmpJVow 7mufNVdSq/ldBzzMtMDQ83Lq3ZmRFKrGk34XQvgqz6HVouGBcxOhyBKDLz5+EooIKp/L Zq2Mj4AE3CwjQandKVsYcND6xV6neakM6Zq2UKkHOtvZvBvxl+C2rpXXgxVnTT5drnSD hDrw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=pYhAzsJ2Du85shITNB+9upl/fTKX2vmS2iYh656fq+w=; b=59ftlyj5aUx0Whsd4wWfv1JRgiFF0M460bbdT4If4JrLwG2ImckR18htaHk9zosGvZ 6qLNEFaOdlcaq38Y763yZ/LnP3Qd4ZJTER6MnNTSyBcQ0i47M+IPa1y59CquU+eR21tT qGRBQDtQfbls+/83+DMMnc0T6n6m+Yyl65Mi9Fql8bcgtXsSDGqD59SaCcVkK7Nx/RMK zQJm2JmIcqFHo6+hcv9zhnbsXKSLsj152U0zOCQf/CrEGpbjgfI2J7NycN6KLJnhGhuG v9183nK9vCi13Z2CRA31DPl+hGsSrLj9jpJ4gtTq7aOKa0NhsahvedAlxvwcDT1euN+J BaKA== X-Gm-Message-State: ACrzQf1Ug4hXSdQphnqsJHhFzDJwP/fgiTXBrTtMv8plQr5AF7zJS1T2 kp8AnVYcXZ2gPjUCP6w+u3XtyA== X-Google-Smtp-Source: AMsMyM7HEwPjmfbhP4V+axQiV2yQn2uMsZ5UqltlgvWRXMxpYf7YRjHCyk/MEN31yXAbro6l3c7leQ== X-Received: by 2002:aa7:9083:0:b0:55f:9827:42e7 with SMTP id i3-20020aa79083000000b0055f982742e7mr5432426pfa.15.1667069355508; Sat, 29 Oct 2022 11:49:15 -0700 (PDT) Received: from localhost.localdomain ([76.242.116.65]) by smtp.gmail.com with ESMTPSA id gb7-20020a17090b060700b0020d9306e735sm1377566pjb.20.2022.10.29.11.49.14 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 29 Oct 2022 11:49:15 -0700 (PDT) From: Roderick Colenbrander X-Google-Original-From: Roderick Colenbrander To: Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, Roderick Colenbrander Subject: [PATCH 02/13] HID: playstation: report DualShock4 hardware and firmware version. Date: Sat, 29 Oct 2022 11:48:40 -0700 Message-Id: <20221029184851.282366-3-roderick.colenbrander@sony.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221029184851.282366-1-roderick.colenbrander@sony.com> References: <20221029184851.282366-1-roderick.colenbrander@sony.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org Report DualShock4 hardware and firmware version info through sysfs. It uses the same sysfs nodes as the DualSense did (and hid-sony). Signed-off-by: Roderick Colenbrander --- drivers/hid/hid-playstation.c | 38 +++++++++++++++++++++++++++++++++++ 1 file changed, 38 insertions(+) diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c index 04d03d2e40db..df4353a4f1e9 100644 --- a/drivers/hid/hid-playstation.c +++ b/drivers/hid/hid-playstation.c @@ -286,6 +286,8 @@ struct dualsense_output_report { #define DS4_INPUT_REPORT_USB 0x01 #define DS4_INPUT_REPORT_USB_SIZE 64 +#define DS4_FEATURE_REPORT_FIRMWARE_INFO 0xa3 +#define DS4_FEATURE_REPORT_FIRMWARE_INFO_SIZE 49 #define DS4_FEATURE_REPORT_PAIRING_INFO 0x12 #define DS4_FEATURE_REPORT_PAIRING_INFO_SIZE 16 @@ -1494,6 +1496,30 @@ static struct ps_device *dualsense_create(struct hid_device *hdev) return ERR_PTR(ret); } +static int dualshock4_get_firmware_info(struct dualshock4 *ds4) +{ + uint8_t *buf; + int ret; + + buf = kzalloc(DS4_FEATURE_REPORT_FIRMWARE_INFO_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = ps_get_report(ds4->base.hdev, DS4_FEATURE_REPORT_FIRMWARE_INFO, buf, + DS4_FEATURE_REPORT_FIRMWARE_INFO_SIZE); + if (ret) { + hid_err(ds4->base.hdev, "Failed to retrieve DualShock4 firmware info: %d\n", ret); + goto err_free; + } + + ds4->base.hw_version = get_unaligned_le16(&buf[35]); + ds4->base.fw_version = get_unaligned_le16(&buf[41]); + +err_free: + kfree(buf); + return ret; +} + static int dualshock4_get_mac_address(struct dualshock4 *ds4) { uint8_t *buf; @@ -1600,6 +1626,12 @@ static struct ps_device *dualshock4_create(struct hid_device *hdev) } snprintf(hdev->uniq, sizeof(hdev->uniq), "%pMR", ds4->base.mac_address); + ret = dualshock4_get_firmware_info(ds4); + if (ret) { + hid_err(hdev, "Failed to get firmware info from DualShock4\n"); + return ERR_PTR(ret); + } + ret = ps_devices_list_add(ps_dev); if (ret) return ERR_PTR(ret); @@ -1616,6 +1648,12 @@ static struct ps_device *dualshock4_create(struct hid_device *hdev) goto err; } + /* + * Reporting hardware and firmware is important as there are frequent updates, which + * can change behavior. + */ + hid_info(hdev, "Registered DualShock4 controller hw_version=0x%08x fw_version=0x%08x\n", + ds4->base.hw_version, ds4->base.fw_version); return &ds4->base; err: From patchwork Sat Oct 29 18:48:41 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roderick Colenbrander X-Patchwork-Id: 620137 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id CF790C38A02 for ; Sat, 29 Oct 2022 18:49:19 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229515AbiJ2StT (ORCPT ); Sat, 29 Oct 2022 14:49:19 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47190 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229562AbiJ2StS (ORCPT ); Sat, 29 Oct 2022 14:49:18 -0400 Received: from mail-pg1-x530.google.com (mail-pg1-x530.google.com [IPv6:2607:f8b0:4864:20::530]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id B3D5B4523F for ; Sat, 29 Oct 2022 11:49:17 -0700 (PDT) Received: by mail-pg1-x530.google.com with SMTP id 20so7489901pgc.5 for ; Sat, 29 Oct 2022 11:49:17 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gaikai-com.20210112.gappssmtp.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=uo6RIOOMyXIxyO79tDm+28Enw9XTfu92Kn4mjGezzMw=; b=qMB3hDDiMMZ2wPvLGwxx6z+Bt7OkozE+aWGuK3npmadnPTNQZ3xeNyubq5tfnLk0th 4057jPvmJ1XToASo9HNFAbF+G08CzIEQwuDEJLGBqYQbWJqig4hCpmUA+hXmZHWZo/tH jFfEHu2dEo0eZkNYPfM4vUVeUjdc1asRUk1pkn56Pt23dDE7eVWYDN5DyFggXpr8nvJg VmYBDTpU1cX2sTpFh4JqsMOM5XVaXRuSUmmL1IctEuXTj9p63wzuDEkWZDe1U6eEaqG4 NDHYLQgwQKB+geaRBhCteEHQ92UDnOAss8X1XT7F8x/SoDPn5LPicgSbOrjqPy8t1Tzj CLPQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=uo6RIOOMyXIxyO79tDm+28Enw9XTfu92Kn4mjGezzMw=; b=DTApmgj0poRTsGaYxgweveRvEqms0Q8qzMG9N0fLVWI/6LlddqpoFSesqpsigHePqf sgIKvJUF7kKnFUmOeSE1IjbeN7TUCULb4Pd89G8uIuGnld2UqiarCi0bDPl9U5d9Y7ic YwTb6/ZDf9K5ZsccWGVdAvppMlQSaD1H+d8o1ZODoseQajjhLh4XLQx0dHzfBQNxfbD1 39/9vTzZA6yu/i9yLAIQ2W/Rvn5ufbHgbArgIrQSWjxEz9a3yU3CKNQpOlYX4MEvn/F4 nKLahfF69R8aGKzgZgr42T9X/E3sOaw5WYi8WdM6NeQ2hTP2am70Q6AMcpaTyR8S1B1J h4dQ== X-Gm-Message-State: ACrzQf3s8NkBYgNAUMaOx8BZ1XbZQdDn4HUNlfHdpTYSRjD00Gk0M4y/ hM+4oPAVFZjfrp6PSBZiT5lyUiAXs8UIHAcM X-Google-Smtp-Source: AMsMyM60IfIYaiAuWG16xOwsbZdpnZ8RLsAEZSAZBlviitM16Hs/CHyAAmdM3suUwHCicrmBjeUHWQ== X-Received: by 2002:a63:5909:0:b0:46e:c98d:e07c with SMTP id n9-20020a635909000000b0046ec98de07cmr5060924pgb.530.1667069357219; Sat, 29 Oct 2022 11:49:17 -0700 (PDT) Received: from localhost.localdomain ([76.242.116.65]) by smtp.gmail.com with ESMTPSA id gb7-20020a17090b060700b0020d9306e735sm1377566pjb.20.2022.10.29.11.49.16 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 29 Oct 2022 11:49:16 -0700 (PDT) From: Roderick Colenbrander X-Google-Original-From: Roderick Colenbrander To: Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, Roderick Colenbrander Subject: [PATCH 03/13] HID: playstation: add DualShock4 battery support. Date: Sat, 29 Oct 2022 11:48:41 -0700 Message-Id: <20221029184851.282366-4-roderick.colenbrander@sony.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221029184851.282366-1-roderick.colenbrander@sony.com> References: <20221029184851.282366-1-roderick.colenbrander@sony.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org Provide DualShock4 battery support through powersupply framework. Signed-off-by: Roderick Colenbrander --- drivers/hid/hid-playstation.c | 70 +++++++++++++++++++++++++++++++++-- 1 file changed, 66 insertions(+), 4 deletions(-) diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c index df4353a4f1e9..20111aa20e86 100644 --- a/drivers/hid/hid-playstation.c +++ b/drivers/hid/hid-playstation.c @@ -291,6 +291,12 @@ struct dualsense_output_report { #define DS4_FEATURE_REPORT_PAIRING_INFO 0x12 #define DS4_FEATURE_REPORT_PAIRING_INFO_SIZE 16 +/* Status field of DualShock4 input report. */ +#define DS4_STATUS0_BATTERY_CAPACITY GENMASK(3, 0) +#define DS4_STATUS0_CABLE_STATE BIT(4) +/* Battery status within batery_status field. */ +#define DS4_BATTERY_STATUS_FULL 11 + struct dualshock4 { struct ps_device base; struct input_dev *gamepad; @@ -302,15 +308,16 @@ struct dualshock4_input_report_common { uint8_t rx, ry; uint8_t buttons[3]; uint8_t z, rz; - uint8_t reserved[20]; + uint8_t status[2]; + uint8_t reserved2; } __packed; -static_assert(sizeof(struct dualshock4_input_report_common) == 29); +static_assert(sizeof(struct dualshock4_input_report_common) == 32); struct dualshock4_input_report_usb { uint8_t report_id; /* 0x01 */ struct dualshock4_input_report_common common; - uint8_t reserved[34]; + uint8_t reserved[31]; } __packed; static_assert(sizeof(struct dualshock4_input_report_usb) == DS4_INPUT_REPORT_USB_SIZE); @@ -1549,7 +1556,9 @@ static int dualshock4_parse_report(struct ps_device *ps_dev, struct hid_report * struct hid_device *hdev = ps_dev->hdev; struct dualshock4 *ds4 = container_of(ps_dev, struct dualshock4, base); struct dualshock4_input_report_common *ds4_report; - uint8_t value; + uint8_t battery_capacity, value; + int battery_status; + unsigned long flags; /* * DualShock4 in USB uses the full HID report for reportID 1, but @@ -1594,6 +1603,53 @@ static int dualshock4_parse_report(struct ps_device *ps_dev, struct hid_report * input_report_key(ds4->gamepad, BTN_MODE, ds4_report->buttons[2] & DS_BUTTONS2_PS_HOME); input_sync(ds4->gamepad); + /* + * Interpretation of the battery_capacity data depends on the cable state. + * When no cable is connected (bit4 is 0): + * - 0:10: percentage in units of 10%. + * When a cable is plugged in: + * - 0-10: percentage in units of 10%. + * - 11: battery is full + * - 14: not charging due to Voltage or temperature error + * - 15: charge error + */ + if (ds4_report->status[0] & DS4_STATUS0_CABLE_STATE) { + uint8_t battery_data = ds4_report->status[0] & DS4_STATUS0_BATTERY_CAPACITY; + + if (battery_data < 10) { + /* Take the mid-point for each battery capacity value, + * because on the hardware side 0 = 0-9%, 1=10-19%, etc. + * This matches official platform behavior, which does + * the same. + */ + battery_capacity = battery_data * 10 + 5; + battery_status = POWER_SUPPLY_STATUS_CHARGING; + } else if (battery_data == 10) { + battery_capacity = 100; + battery_status = POWER_SUPPLY_STATUS_CHARGING; + } else if (battery_data == DS4_BATTERY_STATUS_FULL) { + battery_capacity = 100; + battery_status = POWER_SUPPLY_STATUS_FULL; + } else { /* 14, 15 and undefined values */ + battery_capacity = 0; + battery_status = POWER_SUPPLY_STATUS_UNKNOWN; + } + } else { + uint8_t battery_data = ds4_report->status[0] & DS4_STATUS0_BATTERY_CAPACITY; + + if (battery_data < 10) + battery_capacity = battery_data * 10 + 5; + else /* 10 */ + battery_capacity = 100; + + battery_status = POWER_SUPPLY_STATUS_DISCHARGING; + } + + spin_lock_irqsave(&ps_dev->lock, flags); + ps_dev->battery_capacity = battery_capacity; + ps_dev->battery_status = battery_status; + spin_unlock_irqrestore(&ps_dev->lock, flags); + return 0; } @@ -1616,6 +1672,8 @@ static struct ps_device *dualshock4_create(struct hid_device *hdev) ps_dev = &ds4->base; ps_dev->hdev = hdev; spin_lock_init(&ps_dev->lock); + ps_dev->battery_capacity = 100; /* initial value until parse_report. */ + ps_dev->battery_status = POWER_SUPPLY_STATUS_UNKNOWN; ps_dev->parse_report = dualshock4_parse_report; hid_set_drvdata(hdev, ds4); @@ -1642,6 +1700,10 @@ static struct ps_device *dualshock4_create(struct hid_device *hdev) goto err; } + ret = ps_device_register_battery(ps_dev); + if (ret) + goto err; + ret = ps_device_set_player_id(ps_dev); if (ret) { hid_err(hdev, "Failed to assign player id for DualShock4: %d\n", ret); From patchwork Sat Oct 29 18:48:42 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roderick Colenbrander X-Patchwork-Id: 620136 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 411DBFA3743 for ; Sat, 29 Oct 2022 18:49:24 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229629AbiJ2StW (ORCPT ); Sat, 29 Oct 2022 14:49:22 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47224 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229647AbiJ2StV (ORCPT ); Sat, 29 Oct 2022 14:49:21 -0400 Received: from mail-pf1-x435.google.com (mail-pf1-x435.google.com [IPv6:2607:f8b0:4864:20::435]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 6B6AA459B1 for ; Sat, 29 Oct 2022 11:49:20 -0700 (PDT) Received: by mail-pf1-x435.google.com with SMTP id e4so7440943pfl.2 for ; Sat, 29 Oct 2022 11:49:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gaikai-com.20210112.gappssmtp.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=nAzAvEF71AWk5L+NHqXWv/Wjc0YxY9nJi4/qO8eWe4k=; b=JX+6muPZCY1e8z+Lt1CgekPFYdmFsogUCls+uggySwDSTcdFmRsSWaCUqt0Jo5PYGO umNdWjvgRo42r/ZrZ1eCQQc+W1y/hgxZEj+2cYR9XRHPFhrWipSrnQRfoT2GmrMZr/re VLeGukyYO5m2pBLM8fshMedR5BG96E19cPMByHsY2UJ3Ctgt/bnK5amRDYpLeSnXWmId 1xvmi2y/NlPItXyTsPOVNLYkSSJzvciboyyR6CIcEnLeCQdl//aqsD29Jb8GpScMhj6Q 4dI+vnGk7Kz/BuV5xjtPMdhGF2R5t772WswBCLWYeail6ixu5E8iQyhnP46cO6oxLS69 3Hxg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=nAzAvEF71AWk5L+NHqXWv/Wjc0YxY9nJi4/qO8eWe4k=; b=LYLUshWBzWCkscTxG4XIdxasYgjic5qDH5V5EBDNUB8XPxWXChZW3UgKDhOyyXSIvl qVPSFVf3IcXbwl18SSt5TJsvjwhY38yBPgSX+onEaCbn1JPteLXw7Iz+uhcDmu2N+Gt9 dVA6Rf7NRf3Q/iUs7PAvXcwd7Hnmr++gBNdYHusUXUzvBGkUnF4MUUXfc+cKaXYfamoz IcR88+aXpu7CkZL0HUPjz43RDRdiZLGFObErr4iwajyP0BBVbxF7BeB8i24+Wb03rAnM zpOJjcWBb735GRyQLohdHxGX7S3Cfm36rxR+SBGVtGJ486RQvKa4bt6/XbrixxV4Y3DY YyUQ== X-Gm-Message-State: ACrzQf0ISEUbtUFWlAFI5zdOT6Ow8TtFJKNMZbRb2sVwBNg3aABUVTKN fgka6SVE44Blfu6I0eA2K8SGEuBDn8FZrulk X-Google-Smtp-Source: AMsMyM5HoyX6SdHCoOHu/nobAtnPRTmABnvnJLGBK5aSXvhVzvJBNVhqc4LcG1BoqlbIUnpkts4j0w== X-Received: by 2002:a05:6a00:1ac9:b0:56c:e35b:3ce with SMTP id f9-20020a056a001ac900b0056ce35b03cemr5634267pfv.76.1667069360099; Sat, 29 Oct 2022 11:49:20 -0700 (PDT) Received: from localhost.localdomain ([76.242.116.65]) by smtp.gmail.com with ESMTPSA id gb7-20020a17090b060700b0020d9306e735sm1377566pjb.20.2022.10.29.11.49.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 29 Oct 2022 11:49:19 -0700 (PDT) From: Roderick Colenbrander X-Google-Original-From: Roderick Colenbrander To: Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, Roderick Colenbrander Subject: [PATCH 04/13] HID: playstation: add DualShock4 touchpad support. Date: Sat, 29 Oct 2022 11:48:42 -0700 Message-Id: <20221029184851.282366-5-roderick.colenbrander@sony.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221029184851.282366-1-roderick.colenbrander@sony.com> References: <20221029184851.282366-1-roderick.colenbrander@sony.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org Support the DualShock4 touchpad as a separate input device. The code describes the touchpad input reports through structures similar a bit to the DualSense code. Signed-off-by: Roderick Colenbrander --- drivers/hid/hid-playstation.c | 66 +++++++++++++++++++++++++++++++++-- 1 file changed, 63 insertions(+), 3 deletions(-) diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c index 20111aa20e86..63fc3a67ea74 100644 --- a/drivers/hid/hid-playstation.c +++ b/drivers/hid/hid-playstation.c @@ -291,17 +291,43 @@ struct dualsense_output_report { #define DS4_FEATURE_REPORT_PAIRING_INFO 0x12 #define DS4_FEATURE_REPORT_PAIRING_INFO_SIZE 16 +/* + * Status of a DualShock4 touch point contact. + * Contact IDs, with highest bit set are 'inactive' + * and any associated data is then invalid. + */ +#define DS4_TOUCH_POINT_INACTIVE BIT(7) + /* Status field of DualShock4 input report. */ #define DS4_STATUS0_BATTERY_CAPACITY GENMASK(3, 0) #define DS4_STATUS0_CABLE_STATE BIT(4) /* Battery status within batery_status field. */ #define DS4_BATTERY_STATUS_FULL 11 +/* DualShock4 hardware limits */ +#define DS4_TOUCHPAD_WIDTH 1920 +#define DS4_TOUCHPAD_HEIGHT 942 + struct dualshock4 { struct ps_device base; struct input_dev *gamepad; + struct input_dev *touchpad; }; +struct dualshock4_touch_point { + uint8_t contact; + uint8_t x_lo; + uint8_t x_hi:4, y_lo:4; + uint8_t y_hi; +} __packed; +static_assert(sizeof(struct dualshock4_touch_point) == 4); + +struct dualshock4_touch_report { + uint8_t timestamp; + struct dualshock4_touch_point points[2]; +} __packed; +static_assert(sizeof(struct dualshock4_touch_report) == 9); + /* Main DualShock4 input report excluding any BT/USB specific headers. */ struct dualshock4_input_report_common { uint8_t x, y; @@ -317,7 +343,9 @@ static_assert(sizeof(struct dualshock4_input_report_common) == 32); struct dualshock4_input_report_usb { uint8_t report_id; /* 0x01 */ struct dualshock4_input_report_common common; - uint8_t reserved[31]; + uint8_t num_touch_reports; + struct dualshock4_touch_report touch_reports[3]; + uint8_t reserved[3]; } __packed; static_assert(sizeof(struct dualshock4_input_report_usb) == DS4_INPUT_REPORT_USB_SIZE); @@ -1556,8 +1584,9 @@ static int dualshock4_parse_report(struct ps_device *ps_dev, struct hid_report * struct hid_device *hdev = ps_dev->hdev; struct dualshock4 *ds4 = container_of(ps_dev, struct dualshock4, base); struct dualshock4_input_report_common *ds4_report; - uint8_t battery_capacity, value; - int battery_status; + struct dualshock4_touch_report *touch_reports; + uint8_t battery_capacity, num_touch_reports, value; + int battery_status, i, j; unsigned long flags; /* @@ -1570,6 +1599,8 @@ static int dualshock4_parse_report(struct ps_device *ps_dev, struct hid_report * struct dualshock4_input_report_usb *usb = (struct dualshock4_input_report_usb *)data; ds4_report = &usb->common; + num_touch_reports = usb->num_touch_reports; + touch_reports = usb->touch_reports; } else { hid_err(hdev, "Unhandled reportID=%d\n", report->id); return -1; @@ -1603,6 +1634,29 @@ static int dualshock4_parse_report(struct ps_device *ps_dev, struct hid_report * input_report_key(ds4->gamepad, BTN_MODE, ds4_report->buttons[2] & DS_BUTTONS2_PS_HOME); input_sync(ds4->gamepad); + for (i = 0; i < num_touch_reports; i++) { + struct dualshock4_touch_report *touch_report = &touch_reports[i]; + + for (j = 0; j < ARRAY_SIZE(touch_report->points); j++) { + struct dualshock4_touch_point *point = &touch_report->points[j]; + bool active = (point->contact & DS4_TOUCH_POINT_INACTIVE) ? false : true; + + input_mt_slot(ds4->touchpad, j); + input_mt_report_slot_state(ds4->touchpad, MT_TOOL_FINGER, active); + + if (active) { + int x = (point->x_hi << 8) | point->x_lo; + int y = (point->y_hi << 4) | point->y_lo; + + input_report_abs(ds4->touchpad, ABS_MT_POSITION_X, x); + input_report_abs(ds4->touchpad, ABS_MT_POSITION_Y, y); + } + } + input_mt_sync_frame(ds4->touchpad); + input_sync(ds4->touchpad); + } + input_report_key(ds4->touchpad, BTN_LEFT, ds4_report->buttons[2] & DS_BUTTONS2_TOUCHPAD); + /* * Interpretation of the battery_capacity data depends on the cable state. * When no cable is connected (bit4 is 0): @@ -1700,6 +1754,12 @@ static struct ps_device *dualshock4_create(struct hid_device *hdev) goto err; } + ds4->touchpad = ps_touchpad_create(hdev, DS4_TOUCHPAD_WIDTH, DS4_TOUCHPAD_HEIGHT, 2); + if (IS_ERR(ds4->touchpad)) { + ret = PTR_ERR(ds4->touchpad); + goto err; + } + ret = ps_device_register_battery(ps_dev); if (ret) goto err; From patchwork Sat Oct 29 18:48:43 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roderick Colenbrander X-Patchwork-Id: 619934 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 7CA33C38A02 for ; Sat, 29 Oct 2022 18:49:26 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229692AbiJ2StZ (ORCPT ); Sat, 29 Oct 2022 14:49:25 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47280 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229647AbiJ2StY (ORCPT ); Sat, 29 Oct 2022 14:49:24 -0400 Received: from mail-pg1-x535.google.com (mail-pg1-x535.google.com [IPv6:2607:f8b0:4864:20::535]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id EB7CC45986 for ; Sat, 29 Oct 2022 11:49:23 -0700 (PDT) Received: by mail-pg1-x535.google.com with SMTP id g129so7480455pgc.7 for ; Sat, 29 Oct 2022 11:49:23 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gaikai-com.20210112.gappssmtp.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=NzLSy4hnUQdxSk7oGGKkSfdsYLI93s11XMcS8dFTWaY=; b=JGB7jGPkoSWsjVkScICnRy7Hz7MV5txWfBCgGAp30a63sp+RJoBvBXmVmnWO4JaNko GBAwLQGGrsvv8Q1keB1lSHF8lTM8igr/qFwAsNNC0Q8dDS/l8F3kHMI6eaphcrhpqSKr NKpXwIBdm+x70QTR3ugxDlXvUVPMiSpn/Cuf3vA4xvZsB/VvFtDYY6Jrtz/9YeVfURU9 jLNMxUh2kyOPQFEhhjRCxFLVRD32muCcwV8aXXT0Wrvh2naTszQHjHj3WTekLc3+X+9l JlYHodeSYX5fuN4wwoii1qHj/bepeSKfn2/ac6FsVN5SNn4gmsEwJdHrarQYv186ifzT aC6Q== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=NzLSy4hnUQdxSk7oGGKkSfdsYLI93s11XMcS8dFTWaY=; b=agEaEQVeHyUvkJJJj7ci77e6qOx+46chcW9utMIzyasT2bg1hjnzv8UEIhaJZFhb2k +YSC5eYzZTn5K++uZP7NVfnFYKghdWOWtF7GtN6b3kY5JTqn9ETK+UG3d018WeU0Xqjg MahPIsLfb357M6AXw92calwlM5CVj2ycz4D54GoI3OwR0UbijxV75qP4BAaBeyQkmG/B pRmfWhdx5FJ5WqiIgzPx2kBDunHojvFnYMNhW+pwzlCgYQVEtZ3SrW4EI6xzhfBiXTpA 158ygY0QwqoepVgEENVtVJO9umlzm24Lijyf2lIgicN6h92/M1LphVHl42Gz4aJJZrda JpXQ== X-Gm-Message-State: ACrzQf1uac/IvtFvQWY2z7jAj0qAZrjF7/cJ77H9i9VUl1jEgvsjErZ3 6cHGhyKTiUgL8LBREm5BMcn76w== X-Google-Smtp-Source: AMsMyM5ziuuTclKh/4U7hI6sNWd5JSKuEg3vx4LiojBtgJ/sdYcEAjHE/3P3cfpXo/B+k9255SOCnQ== X-Received: by 2002:a63:5c5a:0:b0:46e:be05:a79a with SMTP id n26-20020a635c5a000000b0046ebe05a79amr5142248pgm.138.1667069363398; Sat, 29 Oct 2022 11:49:23 -0700 (PDT) Received: from localhost.localdomain ([76.242.116.65]) by smtp.gmail.com with ESMTPSA id gb7-20020a17090b060700b0020d9306e735sm1377566pjb.20.2022.10.29.11.49.20 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 29 Oct 2022 11:49:22 -0700 (PDT) From: Roderick Colenbrander X-Google-Original-From: Roderick Colenbrander To: Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, Roderick Colenbrander Subject: [PATCH 05/13] HID: playstation: add DualShock4 accelerometer and gyroscope support. Date: Sat, 29 Oct 2022 11:48:43 -0700 Message-Id: <20221029184851.282366-6-roderick.colenbrander@sony.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221029184851.282366-1-roderick.colenbrander@sony.com> References: <20221029184851.282366-1-roderick.colenbrander@sony.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org Support accelerometer and gyroscope as separate input devices similar how DualSense and hid-sony do it. Signed-off-by: Roderick Colenbrander --- drivers/hid/hid-playstation.c | 170 +++++++++++++++++++++++++++++++++- 1 file changed, 168 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c index 63fc3a67ea74..03f33dea5d93 100644 --- a/drivers/hid/hid-playstation.c +++ b/drivers/hid/hid-playstation.c @@ -286,6 +286,8 @@ struct dualsense_output_report { #define DS4_INPUT_REPORT_USB 0x01 #define DS4_INPUT_REPORT_USB_SIZE 64 +#define DS4_FEATURE_REPORT_CALIBRATION 0x02 +#define DS4_FEATURE_REPORT_CALIBRATION_SIZE 37 #define DS4_FEATURE_REPORT_FIRMWARE_INFO 0xa3 #define DS4_FEATURE_REPORT_FIRMWARE_INFO_SIZE 49 #define DS4_FEATURE_REPORT_PAIRING_INFO 0x12 @@ -305,13 +307,27 @@ struct dualsense_output_report { #define DS4_BATTERY_STATUS_FULL 11 /* DualShock4 hardware limits */ +#define DS4_ACC_RES_PER_G 8192 +#define DS4_ACC_RANGE (4*DS_ACC_RES_PER_G) +#define DS4_GYRO_RES_PER_DEG_S 1024 +#define DS4_GYRO_RANGE (2048*DS_GYRO_RES_PER_DEG_S) #define DS4_TOUCHPAD_WIDTH 1920 #define DS4_TOUCHPAD_HEIGHT 942 struct dualshock4 { struct ps_device base; struct input_dev *gamepad; + struct input_dev *sensors; struct input_dev *touchpad; + + /* Calibration data for accelerometer and gyroscope. */ + struct ps_calibration_data accel_calib_data[3]; + struct ps_calibration_data gyro_calib_data[3]; + + /* Timestamp for sensor data */ + bool sensor_timestamp_initialized; + uint32_t prev_sensor_timestamp; + uint32_t sensor_timestamp_us; }; struct dualshock4_touch_point { @@ -334,9 +350,16 @@ struct dualshock4_input_report_common { uint8_t rx, ry; uint8_t buttons[3]; uint8_t z, rz; - uint8_t reserved[20]; + + /* Motion sensors */ + __le16 sensor_timestamp; + uint8_t sensor_temperature; + __le16 gyro[3]; /* x, y, z */ + __le16 accel[3]; /* x, y, z */ + uint8_t reserved2[5]; + uint8_t status[2]; - uint8_t reserved2; + uint8_t reserved3; } __packed; static_assert(sizeof(struct dualshock4_input_report_common) == 32); @@ -1531,6 +1554,97 @@ static struct ps_device *dualsense_create(struct hid_device *hdev) return ERR_PTR(ret); } +static int dualshock4_get_calibration_data(struct dualshock4 *ds4) +{ + struct hid_device *hdev = ds4->base.hdev; + short gyro_pitch_bias, gyro_pitch_plus, gyro_pitch_minus; + short gyro_yaw_bias, gyro_yaw_plus, gyro_yaw_minus; + short gyro_roll_bias, gyro_roll_plus, gyro_roll_minus; + short gyro_speed_plus, gyro_speed_minus; + short acc_x_plus, acc_x_minus; + short acc_y_plus, acc_y_minus; + short acc_z_plus, acc_z_minus; + int speed_2x; + int range_2g; + int ret = 0; + uint8_t *buf; + + buf = kzalloc(DS4_FEATURE_REPORT_CALIBRATION_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = ps_get_report(hdev, DS4_FEATURE_REPORT_CALIBRATION, buf, + DS4_FEATURE_REPORT_CALIBRATION_SIZE); + if (ret) { + hid_err(hdev, "Failed to retrieve DualShock4 calibration info: %d\n", ret); + goto err_free; + } + + gyro_pitch_bias = get_unaligned_le16(&buf[1]); + gyro_yaw_bias = get_unaligned_le16(&buf[3]); + gyro_roll_bias = get_unaligned_le16(&buf[5]); + gyro_pitch_plus = get_unaligned_le16(&buf[7]); + gyro_pitch_minus = get_unaligned_le16(&buf[9]); + gyro_yaw_plus = get_unaligned_le16(&buf[11]); + gyro_yaw_minus = get_unaligned_le16(&buf[13]); + gyro_roll_plus = get_unaligned_le16(&buf[15]); + gyro_roll_minus = get_unaligned_le16(&buf[17]); + gyro_speed_plus = get_unaligned_le16(&buf[19]); + gyro_speed_minus = get_unaligned_le16(&buf[21]); + acc_x_plus = get_unaligned_le16(&buf[23]); + acc_x_minus = get_unaligned_le16(&buf[25]); + acc_y_plus = get_unaligned_le16(&buf[27]); + acc_y_minus = get_unaligned_le16(&buf[29]); + acc_z_plus = get_unaligned_le16(&buf[31]); + acc_z_minus = get_unaligned_le16(&buf[33]); + + /* + * Set gyroscope calibration and normalization parameters. + * Data values will be normalized to 1/DS4_GYRO_RES_PER_DEG_S degree/s. + */ + speed_2x = (gyro_speed_plus + gyro_speed_minus); + ds4->gyro_calib_data[0].abs_code = ABS_RX; + ds4->gyro_calib_data[0].bias = gyro_pitch_bias; + ds4->gyro_calib_data[0].sens_numer = speed_2x*DS4_GYRO_RES_PER_DEG_S; + ds4->gyro_calib_data[0].sens_denom = gyro_pitch_plus - gyro_pitch_minus; + + ds4->gyro_calib_data[1].abs_code = ABS_RY; + ds4->gyro_calib_data[1].bias = gyro_yaw_bias; + ds4->gyro_calib_data[1].sens_numer = speed_2x*DS4_GYRO_RES_PER_DEG_S; + ds4->gyro_calib_data[1].sens_denom = gyro_yaw_plus - gyro_yaw_minus; + + ds4->gyro_calib_data[2].abs_code = ABS_RZ; + ds4->gyro_calib_data[2].bias = gyro_roll_bias; + ds4->gyro_calib_data[2].sens_numer = speed_2x*DS4_GYRO_RES_PER_DEG_S; + ds4->gyro_calib_data[2].sens_denom = gyro_roll_plus - gyro_roll_minus; + + /* + * Set accelerometer calibration and normalization parameters. + * Data values will be normalized to 1/DS4_ACC_RES_PER_G g. + */ + range_2g = acc_x_plus - acc_x_minus; + ds4->accel_calib_data[0].abs_code = ABS_X; + ds4->accel_calib_data[0].bias = acc_x_plus - range_2g / 2; + ds4->accel_calib_data[0].sens_numer = 2*DS4_ACC_RES_PER_G; + ds4->accel_calib_data[0].sens_denom = range_2g; + + range_2g = acc_y_plus - acc_y_minus; + ds4->accel_calib_data[1].abs_code = ABS_Y; + ds4->accel_calib_data[1].bias = acc_y_plus - range_2g / 2; + ds4->accel_calib_data[1].sens_numer = 2*DS4_ACC_RES_PER_G; + ds4->accel_calib_data[1].sens_denom = range_2g; + + range_2g = acc_z_plus - acc_z_minus; + ds4->accel_calib_data[2].abs_code = ABS_Z; + ds4->accel_calib_data[2].bias = acc_z_plus - range_2g / 2; + ds4->accel_calib_data[2].sens_numer = 2*DS4_ACC_RES_PER_G; + ds4->accel_calib_data[2].sens_denom = range_2g; + +err_free: + kfree(buf); + return ret; +} + static int dualshock4_get_firmware_info(struct dualshock4 *ds4) { uint8_t *buf; @@ -1587,6 +1701,7 @@ static int dualshock4_parse_report(struct ps_device *ps_dev, struct hid_report * struct dualshock4_touch_report *touch_reports; uint8_t battery_capacity, num_touch_reports, value; int battery_status, i, j; + uint16_t sensor_timestamp; unsigned long flags; /* @@ -1634,6 +1749,44 @@ static int dualshock4_parse_report(struct ps_device *ps_dev, struct hid_report * input_report_key(ds4->gamepad, BTN_MODE, ds4_report->buttons[2] & DS_BUTTONS2_PS_HOME); input_sync(ds4->gamepad); + /* Parse and calibrate gyroscope data. */ + for (i = 0; i < ARRAY_SIZE(ds4_report->gyro); i++) { + int raw_data = (short)le16_to_cpu(ds4_report->gyro[i]); + int calib_data = mult_frac(ds4->gyro_calib_data[i].sens_numer, + raw_data - ds4->gyro_calib_data[i].bias, + ds4->gyro_calib_data[i].sens_denom); + + input_report_abs(ds4->sensors, ds4->gyro_calib_data[i].abs_code, calib_data); + } + + /* Parse and calibrate accelerometer data. */ + for (i = 0; i < ARRAY_SIZE(ds4_report->accel); i++) { + int raw_data = (short)le16_to_cpu(ds4_report->accel[i]); + int calib_data = mult_frac(ds4->accel_calib_data[i].sens_numer, + raw_data - ds4->accel_calib_data[i].bias, + ds4->accel_calib_data[i].sens_denom); + + input_report_abs(ds4->sensors, ds4->accel_calib_data[i].abs_code, calib_data); + } + + /* Convert timestamp (in 5.33us unit) to timestamp_us */ + sensor_timestamp = le16_to_cpu(ds4_report->sensor_timestamp); + if (!ds4->sensor_timestamp_initialized) { + ds4->sensor_timestamp_us = DIV_ROUND_CLOSEST(sensor_timestamp*16, 3); + ds4->sensor_timestamp_initialized = true; + } else { + uint16_t delta; + + if (ds4->prev_sensor_timestamp > sensor_timestamp) + delta = (U16_MAX - ds4->prev_sensor_timestamp + sensor_timestamp + 1); + else + delta = sensor_timestamp - ds4->prev_sensor_timestamp; + ds4->sensor_timestamp_us += DIV_ROUND_CLOSEST(delta*16, 3); + } + ds4->prev_sensor_timestamp = sensor_timestamp; + input_event(ds4->sensors, EV_MSC, MSC_TIMESTAMP, ds4->sensor_timestamp_us); + input_sync(ds4->sensors); + for (i = 0; i < num_touch_reports; i++) { struct dualshock4_touch_report *touch_report = &touch_reports[i]; @@ -1748,12 +1901,25 @@ static struct ps_device *dualshock4_create(struct hid_device *hdev) if (ret) return ERR_PTR(ret); + ret = dualshock4_get_calibration_data(ds4); + if (ret) { + hid_err(hdev, "Failed to get calibration data from DualShock4\n"); + goto err; + } + ds4->gamepad = ps_gamepad_create(hdev, NULL); if (IS_ERR(ds4->gamepad)) { ret = PTR_ERR(ds4->gamepad); goto err; } + ds4->sensors = ps_sensors_create(hdev, DS4_ACC_RANGE, DS4_ACC_RES_PER_G, + DS4_GYRO_RANGE, DS4_GYRO_RES_PER_DEG_S); + if (IS_ERR(ds4->sensors)) { + ret = PTR_ERR(ds4->sensors); + goto err; + } + ds4->touchpad = ps_touchpad_create(hdev, DS4_TOUCHPAD_WIDTH, DS4_TOUCHPAD_HEIGHT, 2); if (IS_ERR(ds4->touchpad)) { ret = PTR_ERR(ds4->touchpad); From patchwork Sat Oct 29 18:48:44 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roderick Colenbrander X-Patchwork-Id: 620135 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 528E7C38A02 for ; Sat, 29 Oct 2022 18:49:29 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229647AbiJ2St2 (ORCPT ); Sat, 29 Oct 2022 14:49:28 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47414 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229544AbiJ2St1 (ORCPT ); Sat, 29 Oct 2022 14:49:27 -0400 Received: from mail-pl1-x62d.google.com (mail-pl1-x62d.google.com [IPv6:2607:f8b0:4864:20::62d]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9C523C5 for ; Sat, 29 Oct 2022 11:49:26 -0700 (PDT) Received: by mail-pl1-x62d.google.com with SMTP id p21so3668983plr.7 for ; Sat, 29 Oct 2022 11:49:26 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gaikai-com.20210112.gappssmtp.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=S80tutkX1lAZvu7Kkpft8r04otkjmb2zzvnIOqODTsA=; b=AsHGYbEv3sLbashM+5b6Momx8knoHe49T/WOv0NBkv6nN2ROm0QQ+ntn5k/mbZW2Ew fn4Z4yK2t1ACfy2M3Z7nTAY+F4nYi6wQ0wOYmvgv/+IDNM/KYLm+UYljwPy9P0eWC4Jm 2oYGmGLedPl015++gn307+75HPqoaP3c8WTzcLlE300XNpGIGMOshhQNZ6xOTsbgfWh4 NhOBlDvus0mPw1z+bxjBP6lLs6eZ69YWX8v0KXIPHEK0GjsceGqp9sFaTLG+PxYKxU2q ZJVx31p/1h+CcZxe/xnozfbFHD9Dsi4863ZVfwfJPj/356NWynHD2AnB3GpvcEyr9JZ5 a6iQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=S80tutkX1lAZvu7Kkpft8r04otkjmb2zzvnIOqODTsA=; b=ORKYkC8mru7LMcfgKR1kl6NOAmhy8vA0c3rCf4sYjOSo49E/ljzFYLk+mJ6/gz+uDX Povkg7RY2vg2kx3hlDro1YB2x7oMW5slp1wsARWWaU16GHubgjVUBLWQJNeILQ1DrzP9 cFazK14A2nQ9+D2fCFxwReJU26qJpPtaRQ3JKdtw+yXgD/9OdwmXFVtQAs25NvqL7M5G b+XrV5jNRTamWZVsT/On5qrMtvYS5WoUTkFiCKNIbFZn5cDewoN5q1nEUJ+CjvcI8jMI 4i8NlU2Oh5agH9H4ZOUfqIan0lFDIV8vVbXiAeQdi3vNr/eT2MekzNOQHNEMj9PlXp0b Zjhw== X-Gm-Message-State: ACrzQf2oeTpw8/JAxP1Nf986zV4XmFi9WyZhMNLqi2d8uZdwG4cTPrct iixDTgQjan4bPWSktjEuOtBP3Q== X-Google-Smtp-Source: AMsMyM5yuRzyhesXJF0yQGPVqaXkjF4OJgY51laBj3DdAl+QPpKeHDc/3m17m5OoKI4ui34FUCMHnA== X-Received: by 2002:a17:902:ef52:b0:17c:f072:95bc with SMTP id e18-20020a170902ef5200b0017cf07295bcmr5589989plx.28.1667069366103; Sat, 29 Oct 2022 11:49:26 -0700 (PDT) Received: from localhost.localdomain ([76.242.116.65]) by smtp.gmail.com with ESMTPSA id gb7-20020a17090b060700b0020d9306e735sm1377566pjb.20.2022.10.29.11.49.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 29 Oct 2022 11:49:25 -0700 (PDT) From: Roderick Colenbrander X-Google-Original-From: Roderick Colenbrander To: Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, Roderick Colenbrander Subject: [PATCH 06/13] HID: playstation: Add DualShock4 rumble support. Date: Sat, 29 Oct 2022 11:48:44 -0700 Message-Id: <20221029184851.282366-7-roderick.colenbrander@sony.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221029184851.282366-1-roderick.colenbrander@sony.com> References: <20221029184851.282366-1-roderick.colenbrander@sony.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org This patch implements DualShock4 rumble support in a similar manner as the DualSense implementation. It adds an output worker with granular control of different features of the main DualShock4 output report. Signed-off-by: Roderick Colenbrander --- drivers/hid/hid-playstation.c | 148 +++++++++++++++++++++++++++++++++- 1 file changed, 147 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c index 03f33dea5d93..319f400dd946 100644 --- a/drivers/hid/hid-playstation.c +++ b/drivers/hid/hid-playstation.c @@ -285,6 +285,8 @@ struct dualsense_output_report { #define DS4_INPUT_REPORT_USB 0x01 #define DS4_INPUT_REPORT_USB_SIZE 64 +#define DS4_OUTPUT_REPORT_USB 0x05 +#define DS4_OUTPUT_REPORT_USB_SIZE 32 #define DS4_FEATURE_REPORT_CALIBRATION 0x02 #define DS4_FEATURE_REPORT_CALIBRATION_SIZE 37 @@ -306,6 +308,9 @@ struct dualsense_output_report { /* Battery status within batery_status field. */ #define DS4_BATTERY_STATUS_FULL 11 +/* Flags for DualShock4 output report. */ +#define DS4_OUTPUT_VALID_FLAG0_MOTOR 0x01 + /* DualShock4 hardware limits */ #define DS4_ACC_RES_PER_G 8192 #define DS4_ACC_RANGE (4*DS_ACC_RES_PER_G) @@ -328,6 +333,14 @@ struct dualshock4 { bool sensor_timestamp_initialized; uint32_t prev_sensor_timestamp; uint32_t sensor_timestamp_us; + + bool update_rumble; + uint8_t motor_left; + uint8_t motor_right; + + struct work_struct output_worker; + bool output_worker_initialized; + void *output_report_dmabuf; }; struct dualshock4_touch_point { @@ -372,6 +385,45 @@ struct dualshock4_input_report_usb { } __packed; static_assert(sizeof(struct dualshock4_input_report_usb) == DS4_INPUT_REPORT_USB_SIZE); +/* Common data between Bluetooth and USB DualShock4 output reports. */ +struct dualshock4_output_report_common { + uint8_t valid_flag0; + uint8_t valid_flag1; + + uint8_t reserved; + + uint8_t motor_right; + uint8_t motor_left; + + uint8_t lightbar_red; + uint8_t lightbar_green; + uint8_t lightbar_blue; + uint8_t lightbar_blink_on; + uint8_t lightbar_blink_off; +} __packed; + +struct dualshock4_output_report_usb { + uint8_t report_id; /* 0x5 */ + struct dualshock4_output_report_common common; + uint8_t reserved[21]; +} __packed; +static_assert(sizeof(struct dualshock4_output_report_usb) == DS4_OUTPUT_REPORT_USB_SIZE); + +/* + * The DualShock4 has a main output report used to control most features. It is + * largely the same between Bluetooth and USB except for different headers and CRC. + * This structure hide the differences between the two to simplify sending output reports. + */ +struct dualshock4_output_report { + uint8_t *data; /* Start of data */ + uint8_t len; /* Size of output report */ + + /* Points to USB data payload in case for a USB report else NULL. */ + struct dualshock4_output_report_usb *usb; + /* Points to common section of report, so past any headers. */ + struct dualshock4_output_report_common *common; +}; + /* * Common gamepad buttons across DualShock 3 / 4 and DualSense. * Note: for device with a touchpad, touchpad button is not included @@ -399,6 +451,7 @@ static const struct {int x; int y; } ps_gamepad_hat_mapping[] = { }; static inline void dualsense_schedule_work(struct dualsense *ds); +static inline void dualshock4_schedule_work(struct dualshock4 *ds4); static void dualsense_set_lightbar(struct dualsense *ds, uint8_t red, uint8_t green, uint8_t blue); /* @@ -1692,6 +1745,49 @@ static int dualshock4_get_mac_address(struct dualshock4 *ds4) return ret; } +static void dualshock4_init_output_report(struct dualshock4 *ds4, + struct dualshock4_output_report *rp, void *buf) +{ + struct hid_device *hdev = ds4->base.hdev; + + if (hdev->bus == BUS_USB) { + struct dualshock4_output_report_usb *usb = buf; + + memset(usb, 0, sizeof(*usb)); + usb->report_id = DS4_OUTPUT_REPORT_USB; + + rp->data = buf; + rp->len = sizeof(*usb); + rp->usb = usb; + rp->common = &usb->common; + } +} + +static void dualshock4_output_worker(struct work_struct *work) +{ + struct dualshock4 *ds4 = container_of(work, struct dualshock4, output_worker); + struct dualshock4_output_report report; + struct dualshock4_output_report_common *common; + unsigned long flags; + + dualshock4_init_output_report(ds4, &report, ds4->output_report_dmabuf); + common = report.common; + + spin_lock_irqsave(&ds4->base.lock, flags); + + if (ds4->update_rumble) { + /* Select classic rumble style haptics and enable it. */ + common->valid_flag0 |= DS4_OUTPUT_VALID_FLAG0_MOTOR; + common->motor_left = ds4->motor_left; + common->motor_right = ds4->motor_right; + ds4->update_rumble = false; + } + + spin_unlock_irqrestore(&ds4->base.lock, flags); + + hid_hw_output_report(ds4->base.hdev, report.data, report.len); +} + static int dualshock4_parse_report(struct ps_device *ps_dev, struct hid_report *report, u8 *data, int size) { @@ -1860,10 +1956,52 @@ static int dualshock4_parse_report(struct ps_device *ps_dev, struct hid_report * return 0; } +static int dualshock4_play_effect(struct input_dev *dev, void *data, struct ff_effect *effect) +{ + struct hid_device *hdev = input_get_drvdata(dev); + struct dualshock4 *ds4 = hid_get_drvdata(hdev); + unsigned long flags; + + if (effect->type != FF_RUMBLE) + return 0; + + spin_lock_irqsave(&ds4->base.lock, flags); + ds4->update_rumble = true; + ds4->motor_left = effect->u.rumble.strong_magnitude / 256; + ds4->motor_right = effect->u.rumble.weak_magnitude / 256; + spin_unlock_irqrestore(&ds4->base.lock, flags); + + dualshock4_schedule_work(ds4); + return 0; +} + +static void dualshock4_remove(struct ps_device *ps_dev) +{ + struct dualshock4 *ds4 = container_of(ps_dev, struct dualshock4, base); + unsigned long flags; + + spin_lock_irqsave(&ds4->base.lock, flags); + ds4->output_worker_initialized = false; + spin_unlock_irqrestore(&ds4->base.lock, flags); + + cancel_work_sync(&ds4->output_worker); +} + +static inline void dualshock4_schedule_work(struct dualshock4 *ds4) +{ + unsigned long flags; + + spin_lock_irqsave(&ds4->base.lock, flags); + if (ds4->output_worker_initialized) + schedule_work(&ds4->output_worker); + spin_unlock_irqrestore(&ds4->base.lock, flags); +} + static struct ps_device *dualshock4_create(struct hid_device *hdev) { struct dualshock4 *ds4; struct ps_device *ps_dev; + uint8_t max_output_report_size; int ret; ds4 = devm_kzalloc(&hdev->dev, sizeof(*ds4), GFP_KERNEL); @@ -1882,8 +2020,16 @@ static struct ps_device *dualshock4_create(struct hid_device *hdev) ps_dev->battery_capacity = 100; /* initial value until parse_report. */ ps_dev->battery_status = POWER_SUPPLY_STATUS_UNKNOWN; ps_dev->parse_report = dualshock4_parse_report; + ps_dev->remove = dualshock4_remove; + INIT_WORK(&ds4->output_worker, dualshock4_output_worker); + ds4->output_worker_initialized = true; hid_set_drvdata(hdev, ds4); + max_output_report_size = sizeof(struct dualshock4_output_report_usb); + ds4->output_report_dmabuf = devm_kzalloc(&hdev->dev, max_output_report_size, GFP_KERNEL); + if (!ds4->output_report_dmabuf) + return ERR_PTR(-ENOMEM); + ret = dualshock4_get_mac_address(ds4); if (ret) { hid_err(hdev, "Failed to get MAC address from DualShock4\n"); @@ -1907,7 +2053,7 @@ static struct ps_device *dualshock4_create(struct hid_device *hdev) goto err; } - ds4->gamepad = ps_gamepad_create(hdev, NULL); + ds4->gamepad = ps_gamepad_create(hdev, dualshock4_play_effect); if (IS_ERR(ds4->gamepad)) { ret = PTR_ERR(ds4->gamepad); goto err; From patchwork Sat Oct 29 18:48:45 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roderick Colenbrander X-Patchwork-Id: 619933 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id E158EC38A02 for ; Sat, 29 Oct 2022 18:49:33 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229497AbiJ2Std (ORCPT ); Sat, 29 Oct 2022 14:49:33 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:47818 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229544AbiJ2Stc (ORCPT ); Sat, 29 Oct 2022 14:49:32 -0400 Received: from mail-pg1-x534.google.com (mail-pg1-x534.google.com [IPv6:2607:f8b0:4864:20::534]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 1D37ACC3 for ; Sat, 29 Oct 2022 11:49:30 -0700 (PDT) Received: by mail-pg1-x534.google.com with SMTP id h2so7491628pgp.4 for ; Sat, 29 Oct 2022 11:49:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gaikai-com.20210112.gappssmtp.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=WsDngtzbH8iLQIC4V2H8QEUMeIeA4FhN7pdVWlrDwnY=; b=C9flJKjh3YnHed5QMNQHVgYDXcebkyWCLAPi21gxNCP3wgEQCu/33A+2uTt9lEX1+m veb1Xj/8kM9XUKuIlpWGOeeV6lfbhviOhFePmSyshxCA7/EQrRSQWrBJS3PeqjAT7zfq nebg/wgMlSnAAs3yu46vVBn6ytt7kxt0TYWgObqzJfMKlYhC68Wow63CWZVGWy2MSoCM OqwypJ1TvUScxGFh9eT7Up4c48MD/rV5T9nF69bs1KMCeiaIGzuR4UD8kjQANIntnktU KOqpH5wIf7mx5ECw0BQgXSU7QsHHkaV9OjlvpaYhLtiFGwzNVlHbYX55TSvPO76m+KO8 3E9w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=WsDngtzbH8iLQIC4V2H8QEUMeIeA4FhN7pdVWlrDwnY=; b=sN4dPsjSkj7j3gP288pze+YvyUmAv5Mi+5BKs/VcXt3tYbbUEYwUv+NXVIId2jBzUm Kf9OLL6CQuXXM37SKcl0CN6vwotsVu5NhDsJqZSB7HNgJjiQzy82yWf5tNWC8JtRKlLE GDXxS4DcY5Gq+YeL/U+9myDek3cglxkQImg1MqlF+f3ePMkXPbHcCNQ8y8/hzRLcXEZd nEgKQaY7f8AGxXt5Yr7PYSTSup/lpBrDhuLRUqZ4dNHrlXI4KTSezRStPu+d4UtqdqcM +5UhSw8SVACZR2W14/Z6tBlJItWmE6LcqNmOEml3r5Ps6thVlersmAABhvBAfubVq9rN 0uSQ== X-Gm-Message-State: ACrzQf0tnSBPYGCCgLI2zzr/cqCwXQe76sQ/mPvdHxRfOHnj8UIr9NoN lubN552RXhloqDTI0IzqwX7edw== X-Google-Smtp-Source: AMsMyM7694Ffr+PkrnAiPyd1gcjkekR1nSVkQn4HCKLMQwwOHqNGFO1X9XxT9B5Km/VAE4pIrUYw0Q== X-Received: by 2002:a65:6d86:0:b0:438:f775:b45d with SMTP id bc6-20020a656d86000000b00438f775b45dmr5041912pgb.291.1667069369649; Sat, 29 Oct 2022 11:49:29 -0700 (PDT) Received: from localhost.localdomain ([76.242.116.65]) by smtp.gmail.com with ESMTPSA id gb7-20020a17090b060700b0020d9306e735sm1377566pjb.20.2022.10.29.11.49.26 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 29 Oct 2022 11:49:29 -0700 (PDT) From: Roderick Colenbrander X-Google-Original-From: Roderick Colenbrander To: Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, Roderick Colenbrander Subject: [PATCH 07/13] HID: playstation: make LED brightness adjustable in ps_led_register. Date: Sat, 29 Oct 2022 11:48:45 -0700 Message-Id: <20221029184851.282366-8-roderick.colenbrander@sony.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221029184851.282366-1-roderick.colenbrander@sony.com> References: <20221029184851.282366-1-roderick.colenbrander@sony.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org Make the max_brightness adjustable through ps_led_info struct. This paves the way for a next DualShock4 patch to allow larger brightness values. Signed-off-by: Roderick Colenbrander --- drivers/hid/hid-playstation.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c index 319f400dd946..662c6f220571 100644 --- a/drivers/hid/hid-playstation.c +++ b/drivers/hid/hid-playstation.c @@ -60,6 +60,7 @@ struct ps_calibration_data { struct ps_led_info { const char *name; const char *color; + int max_brightness; enum led_brightness (*brightness_get)(struct led_classdev *cdev); int (*brightness_set)(struct led_classdev *cdev, enum led_brightness); }; @@ -703,7 +704,7 @@ static int ps_led_register(struct ps_device *ps_dev, struct led_classdev *led, return -ENOMEM; led->brightness = 0; - led->max_brightness = 1; + led->max_brightness = led_info->max_brightness; led->flags = LED_CORE_SUSPENDRESUME; led->brightness_get = led_info->brightness_get; led->brightness_set_blocking = led_info->brightness_set; @@ -1459,15 +1460,15 @@ static struct ps_device *dualsense_create(struct hid_device *hdev) int i, ret; static const struct ps_led_info player_leds_info[] = { - { LED_FUNCTION_PLAYER1, "white", dualsense_player_led_get_brightness, + { LED_FUNCTION_PLAYER1, "white", 1, dualsense_player_led_get_brightness, dualsense_player_led_set_brightness }, - { LED_FUNCTION_PLAYER2, "white", dualsense_player_led_get_brightness, + { LED_FUNCTION_PLAYER2, "white", 1, dualsense_player_led_get_brightness, dualsense_player_led_set_brightness }, - { LED_FUNCTION_PLAYER3, "white", dualsense_player_led_get_brightness, + { LED_FUNCTION_PLAYER3, "white", 1, dualsense_player_led_get_brightness, dualsense_player_led_set_brightness }, - { LED_FUNCTION_PLAYER4, "white", dualsense_player_led_get_brightness, + { LED_FUNCTION_PLAYER4, "white", 1, dualsense_player_led_get_brightness, dualsense_player_led_set_brightness }, - { LED_FUNCTION_PLAYER5, "white", dualsense_player_led_get_brightness, + { LED_FUNCTION_PLAYER5, "white", 1, dualsense_player_led_get_brightness, dualsense_player_led_set_brightness } }; From patchwork Sat Oct 29 18:48:46 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roderick Colenbrander X-Patchwork-Id: 620134 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 52527C38A02 for ; Sat, 29 Oct 2022 18:49:36 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229668AbiJ2Stf (ORCPT ); Sat, 29 Oct 2022 14:49:35 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48022 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229544AbiJ2Ste (ORCPT ); Sat, 29 Oct 2022 14:49:34 -0400 Received: from mail-pj1-x1031.google.com (mail-pj1-x1031.google.com [IPv6:2607:f8b0:4864:20::1031]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id DC62210C8 for ; Sat, 29 Oct 2022 11:49:31 -0700 (PDT) Received: by mail-pj1-x1031.google.com with SMTP id l6so7298619pjj.0 for ; Sat, 29 Oct 2022 11:49:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gaikai-com.20210112.gappssmtp.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=9ct8mPy5jUdpktdZbUleJg+LHbiBFLcBSbVV+9zfNac=; b=dRdhJofqOLJDrZse5CnndpvIBSs5m1tL6G6h2FQM/quKZytlxEAFbauHsvkUPHV2v4 4qy6COjmoCFPyW4sihUHnrLHiC0k+6nGeBLwVaM1zjnXcTfpJb/ZoKNmCgGhJZeoUBqy uaNeZJl4CRqxT84ZmvVwYiQuXCHm6zlok5D3KZvXXLZymNZRzAGLJHsHWj01uHIEASoR 83RLFm5IbIg8GgPQvlFB6+rBhnzUdYj7pvqiMsdtBrEvzqsNadIxPfc4GZXYa9h5kzbp yRswHfxB5KeECvSei2Um/kWglMD9powU8KtMP9pYxCDdwSfX/88rJUdE02+6vFRsDxJf rLZQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=9ct8mPy5jUdpktdZbUleJg+LHbiBFLcBSbVV+9zfNac=; b=MLXHKI9ULuhCQQY/4JnaiDZoE89wNqoacTffCQoA2B+V1/gX9vQwZtoCvJe+d/d0nL o+q+AU3d7gqjc7h3TMAb2TptVaopELjaN/smBwpCOSiSy/eu8WTjkzIMsHz6k4pPiHTD ZCyM3IBi5du2FJpV8j8QyPrsN3gzg09uHBEdsMbr+UPbtyG5VyMHb4c3npJLfCIr+4Y8 C6e+Q0bzdXJTPS6snEZ3aVhPYJoXJR+gFHjEOd51UBE9XV7DvV4RWPixcQcHW/diqjXQ VnC09sNQ/eYdM32qvg9SpoZacfA3J3aoEyGHED/MNmOGtf8YgkPmD18Xlx2z6ScuXL7Q 4GFA== X-Gm-Message-State: ACrzQf2N/bwAR0kM65rwqFFY9nS/geAsTXXVL03cRhFUf1wORx4NYEhZ TYIHil2YpQbdkDys+msIj9DNvQ== X-Google-Smtp-Source: AMsMyM5PG3P1Lzo4Hh6ORXfHEasAaw/KPzZO+75UEK+iGVUqgEprVH3RPE8shx16tmfQCjxVbwUHaQ== X-Received: by 2002:a17:90b:4a8d:b0:20d:402d:6153 with SMTP id lp13-20020a17090b4a8d00b0020d402d6153mr6113561pjb.44.1667069371363; Sat, 29 Oct 2022 11:49:31 -0700 (PDT) Received: from localhost.localdomain ([76.242.116.65]) by smtp.gmail.com with ESMTPSA id gb7-20020a17090b060700b0020d9306e735sm1377566pjb.20.2022.10.29.11.49.29 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 29 Oct 2022 11:49:30 -0700 (PDT) From: Roderick Colenbrander X-Google-Original-From: Roderick Colenbrander To: Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, Roderick Colenbrander Subject: [PATCH 08/13] HID: playstation: support DualShock4 lightbar. Date: Sat, 29 Oct 2022 11:48:46 -0700 Message-Id: <20221029184851.282366-9-roderick.colenbrander@sony.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221029184851.282366-1-roderick.colenbrander@sony.com> References: <20221029184851.282366-1-roderick.colenbrander@sony.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org Expose the lightbar LEDs in the same manner as hid-sony through individual LEDs for backwards compatibility reasons. There is a slight change in LED naming to use the input device name as opposed to the MAC address like hid-sony did. This is expected to not cause any issues and should make the naming more compliant. In addition set a default lightbar color based on player ID. Signed-off-by: Roderick Colenbrander --- drivers/hid/hid-playstation.c | 141 +++++++++++++++++++++++++++++++++- 1 file changed, 138 insertions(+), 3 deletions(-) diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c index 662c6f220571..d42fda13580a 100644 --- a/drivers/hid/hid-playstation.c +++ b/drivers/hid/hid-playstation.c @@ -311,6 +311,8 @@ struct dualsense_output_report { /* Flags for DualShock4 output report. */ #define DS4_OUTPUT_VALID_FLAG0_MOTOR 0x01 +#define DS4_OUTPUT_VALID_FLAG0_LED 0x02 +#define DS4_OUTPUT_VALID_FLAG0_LED_BLINK 0x04 /* DualShock4 hardware limits */ #define DS4_ACC_RES_PER_G 8192 @@ -339,6 +341,14 @@ struct dualshock4 { uint8_t motor_left; uint8_t motor_right; + /* Lightbar leds */ + bool update_lightbar; + bool lightbar_enabled; /* For use by global LED control. */ + uint8_t lightbar_red; + uint8_t lightbar_green; + uint8_t lightbar_blue; + struct led_classdev lightbar_leds[4]; + struct work_struct output_worker; bool output_worker_initialized; void *output_report_dmabuf; @@ -697,8 +707,14 @@ static int ps_led_register(struct ps_device *ps_dev, struct led_classdev *led, { int ret; - led->name = devm_kasprintf(&ps_dev->hdev->dev, GFP_KERNEL, - "%s:%s:%s", ps_dev->input_dev_name, led_info->color, led_info->name); + if (led_info->name) { + led->name = devm_kasprintf(&ps_dev->hdev->dev, GFP_KERNEL, + "%s:%s:%s", ps_dev->input_dev_name, led_info->color, led_info->name); + } else { + /* Backwards compatible mode for hid-sony, but not compliant with LED class spec. */ + led->name = devm_kasprintf(&ps_dev->hdev->dev, GFP_KERNEL, + "%s:%s", ps_dev->input_dev_name, led_info->color); + } if (!led->name) return -ENOMEM; @@ -1746,6 +1762,60 @@ static int dualshock4_get_mac_address(struct dualshock4 *ds4) return ret; } +static enum led_brightness dualshock4_led_get_brightness(struct led_classdev *led) +{ + struct hid_device *hdev = to_hid_device(led->dev->parent); + struct dualshock4 *ds4 = hid_get_drvdata(hdev); + unsigned int led_index; + + led_index = led - ds4->lightbar_leds; + switch (led_index) { + case 0: + return ds4->lightbar_red; + case 1: + return ds4->lightbar_green; + case 2: + return ds4->lightbar_blue; + case 3: + return ds4->lightbar_enabled; + } + + return -1; +} + +static int dualshock4_led_set_brightness(struct led_classdev *led, enum led_brightness value) +{ + struct hid_device *hdev = to_hid_device(led->dev->parent); + struct dualshock4 *ds4 = hid_get_drvdata(hdev); + unsigned long flags; + unsigned int led_index; + + spin_lock_irqsave(&ds4->base.lock, flags); + + led_index = led - ds4->lightbar_leds; + switch (led_index) { + case 0: + ds4->lightbar_red = value; + break; + case 1: + ds4->lightbar_green = value; + break; + case 2: + ds4->lightbar_blue = value; + break; + case 3: + ds4->lightbar_enabled = !!value; + } + + ds4->update_lightbar = true; + + spin_unlock_irqrestore(&ds4->base.lock, flags); + + dualshock4_schedule_work(ds4); + + return 0; +} + static void dualshock4_init_output_report(struct dualshock4 *ds4, struct dualshock4_output_report *rp, void *buf) { @@ -1784,6 +1854,18 @@ static void dualshock4_output_worker(struct work_struct *work) ds4->update_rumble = false; } + if (ds4->update_lightbar) { + common->valid_flag0 |= DS4_OUTPUT_VALID_FLAG0_LED; + /* Comptabile behavior with hid-sony, which used a dummy global LED to + * allow enabling/disabling the lightbar. The global LED maps to + * lightbar_enabled. + */ + common->lightbar_red = ds4->lightbar_enabled ? ds4->lightbar_red : 0; + common->lightbar_green = ds4->lightbar_enabled ? ds4->lightbar_green : 0; + common->lightbar_blue = ds4->lightbar_enabled ? ds4->lightbar_blue : 0; + ds4->update_lightbar = false; + } + spin_unlock_irqrestore(&ds4->base.lock, flags); hid_hw_output_report(ds4->base.hdev, report.data, report.len); @@ -1998,12 +2080,52 @@ static inline void dualshock4_schedule_work(struct dualshock4 *ds4) spin_unlock_irqrestore(&ds4->base.lock, flags); } +/* Set default lightbar color based on player. */ +static void dualshock4_set_default_lightbar_colors(struct dualshock4 *ds4) +{ + /* Use same player colors as PlayStation 4. + * Array of colors is in RGB. + */ + static const int player_colors[4][3] = { + { 0x00, 0x00, 0x40 }, /* Blue */ + { 0x40, 0x00, 0x00 }, /* Red */ + { 0x00, 0x40, 0x00 }, /* Green */ + { 0x20, 0x00, 0x20 } /* Pink */ + }; + + uint8_t player_id = ds4->base.player_id % ARRAY_SIZE(player_colors); + + ds4->lightbar_enabled = true; + ds4->lightbar_red = player_colors[player_id][0]; + ds4->lightbar_green = player_colors[player_id][1]; + ds4->lightbar_blue = player_colors[player_id][2]; + + ds4->update_lightbar = true; + dualshock4_schedule_work(ds4); +} + static struct ps_device *dualshock4_create(struct hid_device *hdev) { struct dualshock4 *ds4; struct ps_device *ps_dev; uint8_t max_output_report_size; - int ret; + int i, ret; + + /* The DualShock4 has an RGB lightbar, which the original hid-sony driver + * exposed as a set of 4 LEDs for the 3 color channels and a global control. + * Ideally this should have used the multi-color LED class, which didn't exist + * yet. In addition the driver used a naming scheme not compliant with the LED + * naming spec by using ":", which contained many colons. + * We use a more compliant by using ":" name now. Ideally + * would have been "::indicator", but that would break + * existing applications (e.g. Android). Nothing matches against MAC address. + */ + static const struct ps_led_info lightbar_leds_info[] = { + { NULL, "red", 255, dualshock4_led_get_brightness, dualshock4_led_set_brightness }, + { NULL, "green", 255, dualshock4_led_get_brightness, dualshock4_led_set_brightness }, + { NULL, "blue", 255, dualshock4_led_get_brightness, dualshock4_led_set_brightness }, + { NULL, "global", 1, dualshock4_led_get_brightness, dualshock4_led_set_brightness }, + }; ds4 = devm_kzalloc(&hdev->dev, sizeof(*ds4), GFP_KERNEL); if (!ds4) @@ -2060,6 +2182,9 @@ static struct ps_device *dualshock4_create(struct hid_device *hdev) goto err; } + /* Use gamepad input device name as primary device name for e.g. LEDs */ + ps_dev->input_dev_name = dev_name(&ds4->gamepad->dev); + ds4->sensors = ps_sensors_create(hdev, DS4_ACC_RANGE, DS4_ACC_RES_PER_G, DS4_GYRO_RANGE, DS4_GYRO_RES_PER_DEG_S); if (IS_ERR(ds4->sensors)) { @@ -2077,12 +2202,22 @@ static struct ps_device *dualshock4_create(struct hid_device *hdev) if (ret) goto err; + for (i = 0; i < ARRAY_SIZE(lightbar_leds_info); i++) { + const struct ps_led_info *led_info = &lightbar_leds_info[i]; + + ret = ps_led_register(ps_dev, &ds4->lightbar_leds[i], led_info); + if (ret < 0) + goto err; + } + ret = ps_device_set_player_id(ps_dev); if (ret) { hid_err(hdev, "Failed to assign player id for DualShock4: %d\n", ret); goto err; } + dualshock4_set_default_lightbar_colors(ds4); + /* * Reporting hardware and firmware is important as there are frequent updates, which * can change behavior. From patchwork Sat Oct 29 18:48:47 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roderick Colenbrander X-Patchwork-Id: 619932 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 992A3C433FE for ; Sat, 29 Oct 2022 18:49:38 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229670AbiJ2Sth (ORCPT ); Sat, 29 Oct 2022 14:49:37 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48240 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229544AbiJ2Sth (ORCPT ); Sat, 29 Oct 2022 14:49:37 -0400 Received: from mail-pf1-x42b.google.com (mail-pf1-x42b.google.com [IPv6:2607:f8b0:4864:20::42b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 98A2F2702 for ; Sat, 29 Oct 2022 11:49:35 -0700 (PDT) Received: by mail-pf1-x42b.google.com with SMTP id v28so7244597pfi.12 for ; Sat, 29 Oct 2022 11:49:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gaikai-com.20210112.gappssmtp.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=MN5CjeIRoKJqllg1PKNfRnorANb6qtmvfQkjbsjTi4c=; b=e9RCUF4GXkm4Jwx/IFRrNrjqH90bss1Bty+U05RH8zEQIlFnvtBGfE98qn3mgoXtIC fr60ivyhvzjq3uGmShSvEgtLOtXVHHdbUSN6Gage5h+QPQx44IBq2pd21JPKMVQQ8akZ j8Yk6J4njtODK7LNptkr3bUI5A75K4VOGnjgCsQpFgt6sGhFGNTfKoRcJ8vLdJC3NzIS BHY71As0XZ+ZgaUHxCF2qQrrypfgHHCeWTKIdfwIi1iXq2wv4JEjFYEwdgEoY6ipUoO3 DJwWTBxLjFtqJb3GdX1M4PPDsdKGV9bi5vSgn+j2HKpWT7eBAr12qjDEAe8d8BxR6mki wLeQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=MN5CjeIRoKJqllg1PKNfRnorANb6qtmvfQkjbsjTi4c=; b=NoOzq6hzo/OvxFA1kZhjJaWzwz94DMHbh+UVx/rAPVdQoGj0Cblo5IyJC9M2N3kYwb mkdSx/QGe5Lpv7fm9O1ECRgKKW0UYzIOh9sn/HF/hFQJHZlIZmy2Eb+GFQe/9hhycaej vOvYHQ/61Y9WU0Jg/kATyJteSDwVP7p7ANYU+kRvtMakl+OWBZ4DY8MGfZ2+EBuPnEnb p40OTD3cJCAObewumzaHzQUjgMczBnWQPF3GOpEzjzqUVc/eKgToJTUuGQfEIBTUhEBC NvA1q3Ry58oPI91xY3Z8ihYm02cTYvk+HtFHtNxc3t9kuv2HDr8vaj6SYyJ1UeAc0IqA vqDg== X-Gm-Message-State: ACrzQf2TjAEipLQeX162s/nvpiWlKGq/U8XBe71MgXoWdWa88j4yPbK4 9aHrNXD+CHDLVGSJWzf7Go+vQIc9Yy1Js8jg X-Google-Smtp-Source: AMsMyM6qnVShrbmJT3XMkSnqBaeVXpRmZOJRbwZeMy2W/W0LL82mnTiT5oMr8bL/TC8hU/0VUVftsw== X-Received: by 2002:aa7:8887:0:b0:56d:41a9:dbb7 with SMTP id z7-20020aa78887000000b0056d41a9dbb7mr288156pfe.82.1667069374733; Sat, 29 Oct 2022 11:49:34 -0700 (PDT) Received: from localhost.localdomain ([76.242.116.65]) by smtp.gmail.com with ESMTPSA id gb7-20020a17090b060700b0020d9306e735sm1377566pjb.20.2022.10.29.11.49.33 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 29 Oct 2022 11:49:34 -0700 (PDT) From: Roderick Colenbrander X-Google-Original-From: Roderick Colenbrander To: Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, Roderick Colenbrander Subject: [PATCH 09/13] HID: playstation: support DualShock4 lightbar blink. Date: Sat, 29 Oct 2022 11:48:47 -0700 Message-Id: <20221029184851.282366-10-roderick.colenbrander@sony.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221029184851.282366-1-roderick.colenbrander@sony.com> References: <20221029184851.282366-1-roderick.colenbrander@sony.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org Support lightbar blink through LEDs framework. Signed-off-by: Roderick Colenbrander --- drivers/hid/hid-playstation.c | 47 ++++++++++++++++++++++++++++++++++- 1 file changed, 46 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c index d42fda13580a..7ceb37f04d24 100644 --- a/drivers/hid/hid-playstation.c +++ b/drivers/hid/hid-playstation.c @@ -63,6 +63,7 @@ struct ps_led_info { int max_brightness; enum led_brightness (*brightness_get)(struct led_classdev *cdev); int (*brightness_set)(struct led_classdev *cdev, enum led_brightness); + int (*blink_set)(struct led_classdev *led, unsigned long *on, unsigned long *off); }; /* Seed values for DualShock4 / DualSense CRC32 for different report types. */ @@ -319,6 +320,7 @@ struct dualsense_output_report { #define DS4_ACC_RANGE (4*DS_ACC_RES_PER_G) #define DS4_GYRO_RES_PER_DEG_S 1024 #define DS4_GYRO_RANGE (2048*DS_GYRO_RES_PER_DEG_S) +#define DS4_LIGHTBAR_MAX_BLINK 255 /* 255 centiseconds */ #define DS4_TOUCHPAD_WIDTH 1920 #define DS4_TOUCHPAD_HEIGHT 942 @@ -343,10 +345,13 @@ struct dualshock4 { /* Lightbar leds */ bool update_lightbar; + bool update_lightbar_blink; bool lightbar_enabled; /* For use by global LED control. */ uint8_t lightbar_red; uint8_t lightbar_green; uint8_t lightbar_blue; + uint8_t lightbar_blink_on; /* In increments of 10ms. */ + uint8_t lightbar_blink_off; /* In increments of 10ms. */ struct led_classdev lightbar_leds[4]; struct work_struct output_worker; @@ -724,6 +729,7 @@ static int ps_led_register(struct ps_device *ps_dev, struct led_classdev *led, led->flags = LED_CORE_SUSPENDRESUME; led->brightness_get = led_info->brightness_get; led->brightness_set_blocking = led_info->brightness_set; + led->blink_set = led_info->blink_set; ret = devm_led_classdev_register(&ps_dev->hdev->dev, led); if (ret) { @@ -1783,6 +1789,37 @@ static enum led_brightness dualshock4_led_get_brightness(struct led_classdev *le return -1; } +static int dualshock4_led_set_blink(struct led_classdev *led, unsigned long *delay_on, + unsigned long *delay_off) +{ + struct hid_device *hdev = to_hid_device(led->dev->parent); + struct dualshock4 *ds4 = hid_get_drvdata(hdev); + unsigned long flags; + + spin_lock_irqsave(&ds4->base.lock, flags); + + if (!*delay_on && !*delay_off) { + /* Default to 1 Hz (50 centiseconds on, 50 centiseconds off). */ + ds4->lightbar_blink_on = 50; + ds4->lightbar_blink_off = 50; + } else { + /* Blink delays in centiseconds. */ + ds4->lightbar_blink_on = min_t(unsigned long, *delay_on/10, DS4_LIGHTBAR_MAX_BLINK); + ds4->lightbar_blink_off = min_t(unsigned long, *delay_off/10, DS4_LIGHTBAR_MAX_BLINK); + } + + ds4->update_lightbar_blink = true; + + spin_unlock_irqrestore(&ds4->base.lock, flags); + + dualshock4_schedule_work(ds4); + + *delay_on = ds4->lightbar_blink_on; + *delay_off = ds4->lightbar_blink_off; + + return 0; +} + static int dualshock4_led_set_brightness(struct led_classdev *led, enum led_brightness value) { struct hid_device *hdev = to_hid_device(led->dev->parent); @@ -1866,6 +1903,13 @@ static void dualshock4_output_worker(struct work_struct *work) ds4->update_lightbar = false; } + if (ds4->update_lightbar_blink) { + common->valid_flag0 |= DS4_OUTPUT_VALID_FLAG0_LED_BLINK; + common->lightbar_blink_on = ds4->lightbar_blink_on; + common->lightbar_blink_off = ds4->lightbar_blink_off; + ds4->update_lightbar_blink = false; + } + spin_unlock_irqrestore(&ds4->base.lock, flags); hid_hw_output_report(ds4->base.hdev, report.data, report.len); @@ -2124,7 +2168,8 @@ static struct ps_device *dualshock4_create(struct hid_device *hdev) { NULL, "red", 255, dualshock4_led_get_brightness, dualshock4_led_set_brightness }, { NULL, "green", 255, dualshock4_led_get_brightness, dualshock4_led_set_brightness }, { NULL, "blue", 255, dualshock4_led_get_brightness, dualshock4_led_set_brightness }, - { NULL, "global", 1, dualshock4_led_get_brightness, dualshock4_led_set_brightness }, + { NULL, "global", 1, dualshock4_led_get_brightness, dualshock4_led_set_brightness, + dualshock4_led_set_blink }, }; ds4 = devm_kzalloc(&hdev->dev, sizeof(*ds4), GFP_KERNEL); From patchwork Sat Oct 29 18:48:48 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roderick Colenbrander X-Patchwork-Id: 620133 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9B44DFA3743 for ; Sat, 29 Oct 2022 18:49:40 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229674AbiJ2Stj (ORCPT ); Sat, 29 Oct 2022 14:49:39 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48420 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229544AbiJ2Stj (ORCPT ); Sat, 29 Oct 2022 14:49:39 -0400 Received: from mail-pf1-x42f.google.com (mail-pf1-x42f.google.com [IPv6:2607:f8b0:4864:20::42f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 725912702 for ; Sat, 29 Oct 2022 11:49:37 -0700 (PDT) Received: by mail-pf1-x42f.google.com with SMTP id g62so7413370pfb.10 for ; Sat, 29 Oct 2022 11:49:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gaikai-com.20210112.gappssmtp.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=0QK+eBK7A6uWNiH4WHfbTPt08tvNWgQQn7I20CNk5Tk=; b=V0h4mmVGqIw514TRn+FHb4/ivL9mri5VZTFAGBwJIikaA3iCpTJ9Xx0Ds4sf0YVRJn vEAvXj60SrlEBVjcTkgRW8/0iCyDBU65kvKiUH+ccJvq/ufaLFie5dCcCm0Ai5y6QMlK vAwRzp+r5SUlKbJY55FIknqHtJkeJD+tgfx7cSFB1sbacLEJtmD2D+69BJq4N6wPi5xs zFXpQWHEBsFSiMZ2vM3d+8Ev2r27lDsDBSxT/uMq3GKdArzIbQUCO6/UWWYJCTw0uT9/ YddMLKTVUF4BtuqoCLmB0ouVEYvDJfVLM+KhQ1vVYR6oC3QStFGMg3VbD+EYFskhQQ6k dp0w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=0QK+eBK7A6uWNiH4WHfbTPt08tvNWgQQn7I20CNk5Tk=; b=EaySLaZHSRvN8HqmsC1czhTvehtD6noeTL8n9uXdYT/1Xy3TcdEnPNHTwGyrULzspw YoIIoT8GIzQ5VVcHgX/A/2L5TLnFRcUsunCUQj0jVBEqTSUnRKKHxuXJLS+ZluO/lIyk Fp83pOuLRrpZK9DgpkAjqpx2LDscttPkqkLukyhXhqs5UTHfuNnBhJjB+1R5BAcJmJko mnbzaiTS97KeatxdraiXFF7tt58v8ixJAgsjWMPRxkK2fxyixM9Kk45U2GRU3plpMH5m qyMR9L4LpFT0c6j/7vnCsRhkBEvpFsfH8pZVN7BnzGxVXpIHVigHmgU40fmIToUKC47J QAFg== X-Gm-Message-State: ACrzQf3saliHhfCE7qdu3ybpyyWMc2Sw+XMJXfCiQpIMBBCphiUs1Mys DvKKvO75PsCqec9oOK92D7viDg== X-Google-Smtp-Source: AMsMyM4tpQk7pCXaQBhnd/iYcVz8hbWWNMaM4HuYNjBUJ4IsrLyVbrems0TeyMyZgFbrTZONoz0WOg== X-Received: by 2002:a63:5063:0:b0:46e:cd36:ce0c with SMTP id q35-20020a635063000000b0046ecd36ce0cmr5114494pgl.617.1667069376917; Sat, 29 Oct 2022 11:49:36 -0700 (PDT) Received: from localhost.localdomain ([76.242.116.65]) by smtp.gmail.com with ESMTPSA id gb7-20020a17090b060700b0020d9306e735sm1377566pjb.20.2022.10.29.11.49.35 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 29 Oct 2022 11:49:36 -0700 (PDT) From: Roderick Colenbrander X-Google-Original-From: Roderick Colenbrander To: Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, Roderick Colenbrander Subject: [PATCH 10/13] HID: playstation: add option to ignore CRC in ps_get_report. Date: Sat, 29 Oct 2022 11:48:48 -0700 Message-Id: <20221029184851.282366-11-roderick.colenbrander@sony.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221029184851.282366-1-roderick.colenbrander@sony.com> References: <20221029184851.282366-1-roderick.colenbrander@sony.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org This patch adds a parameter to ps_get_report to ignore CRC checks. This prepares for DualShock4, which has some HID reports, which lack CRC. Signed-off-by: Roderick Colenbrander --- drivers/hid/hid-playstation.c | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c index 7ceb37f04d24..81a12aafed17 100644 --- a/drivers/hid/hid-playstation.c +++ b/drivers/hid/hid-playstation.c @@ -672,7 +672,8 @@ static struct input_dev *ps_gamepad_create(struct hid_device *hdev, return gamepad; } -static int ps_get_report(struct hid_device *hdev, uint8_t report_id, uint8_t *buf, size_t size) +static int ps_get_report(struct hid_device *hdev, uint8_t report_id, uint8_t *buf, size_t size, + bool check_crc) { int ret; @@ -693,7 +694,7 @@ static int ps_get_report(struct hid_device *hdev, uint8_t report_id, uint8_t *bu return -EINVAL; } - if (hdev->bus == BUS_BLUETOOTH) { + if (hdev->bus == BUS_BLUETOOTH && check_crc) { /* Last 4 bytes contains crc32. */ uint8_t crc_offset = size - 4; uint32_t report_crc = get_unaligned_le32(&buf[crc_offset]); @@ -894,7 +895,7 @@ static int dualsense_get_calibration_data(struct dualsense *ds) return -ENOMEM; ret = ps_get_report(ds->base.hdev, DS_FEATURE_REPORT_CALIBRATION, buf, - DS_FEATURE_REPORT_CALIBRATION_SIZE); + DS_FEATURE_REPORT_CALIBRATION_SIZE, true); if (ret) { hid_err(ds->base.hdev, "Failed to retrieve DualSense calibration info: %d\n", ret); goto err_free; @@ -976,7 +977,7 @@ static int dualsense_get_firmware_info(struct dualsense *ds) return -ENOMEM; ret = ps_get_report(ds->base.hdev, DS_FEATURE_REPORT_FIRMWARE_INFO, buf, - DS_FEATURE_REPORT_FIRMWARE_INFO_SIZE); + DS_FEATURE_REPORT_FIRMWARE_INFO_SIZE, true); if (ret) { hid_err(ds->base.hdev, "Failed to retrieve DualSense firmware info: %d\n", ret); goto err_free; @@ -1009,7 +1010,7 @@ static int dualsense_get_mac_address(struct dualsense *ds) return -ENOMEM; ret = ps_get_report(ds->base.hdev, DS_FEATURE_REPORT_PAIRING_INFO, buf, - DS_FEATURE_REPORT_PAIRING_INFO_SIZE); + DS_FEATURE_REPORT_PAIRING_INFO_SIZE, true); if (ret) { hid_err(ds->base.hdev, "Failed to retrieve DualSense pairing info: %d\n", ret); goto err_free; @@ -1650,7 +1651,7 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4) return -ENOMEM; ret = ps_get_report(hdev, DS4_FEATURE_REPORT_CALIBRATION, buf, - DS4_FEATURE_REPORT_CALIBRATION_SIZE); + DS4_FEATURE_REPORT_CALIBRATION_SIZE, true); if (ret) { hid_err(hdev, "Failed to retrieve DualShock4 calibration info: %d\n", ret); goto err_free; @@ -1731,7 +1732,7 @@ static int dualshock4_get_firmware_info(struct dualshock4 *ds4) return -ENOMEM; ret = ps_get_report(ds4->base.hdev, DS4_FEATURE_REPORT_FIRMWARE_INFO, buf, - DS4_FEATURE_REPORT_FIRMWARE_INFO_SIZE); + DS4_FEATURE_REPORT_FIRMWARE_INFO_SIZE, true); if (ret) { hid_err(ds4->base.hdev, "Failed to retrieve DualShock4 firmware info: %d\n", ret); goto err_free; @@ -1755,7 +1756,7 @@ static int dualshock4_get_mac_address(struct dualshock4 *ds4) return -ENOMEM; ret = ps_get_report(ds4->base.hdev, DS4_FEATURE_REPORT_PAIRING_INFO, buf, - DS4_FEATURE_REPORT_PAIRING_INFO_SIZE); + DS4_FEATURE_REPORT_PAIRING_INFO_SIZE, true); if (ret) { hid_err(ds4->base.hdev, "Failed to retrieve DualShock4 pairing info: %d\n", ret); goto err_free; From patchwork Sat Oct 29 18:48:49 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roderick Colenbrander X-Patchwork-Id: 619931 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id DA8BFC38A02 for ; Sat, 29 Oct 2022 18:49:44 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229544AbiJ2Stn (ORCPT ); Sat, 29 Oct 2022 14:49:43 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:48810 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229720AbiJ2Stn (ORCPT ); Sat, 29 Oct 2022 14:49:43 -0400 Received: from mail-pl1-x62f.google.com (mail-pl1-x62f.google.com [IPv6:2607:f8b0:4864:20::62f]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id A4E17E08C for ; Sat, 29 Oct 2022 11:49:41 -0700 (PDT) Received: by mail-pl1-x62f.google.com with SMTP id j12so7539090plj.5 for ; Sat, 29 Oct 2022 11:49:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gaikai-com.20210112.gappssmtp.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=J36xzuyhBLKi0VZ+Hws5Z6a9JxZxNltccSD7cDPx5q8=; b=5ywxbjDFHGEI974Vu2LnXKDz3IfeCWMUJIT/O2e4aJ1Fka5tHaTd88ccRolD8U375X kcrowyDf66l/9WnCUX0B0D954MlwKoZummyE1b5veveBjHGP0b2wGWS0LJhxN5ynlawb RO08kmhS6jzo/fLx44TCqEbG36hqgmsqVlDzzcoDVc0Oyk91rFZfjHlToByL+dbRp0ZJ phMCWJafSZHgY/GtdegRpZ+v1tnv948NK/hOtPj6WRcPt4/RCwWXzIcHFHW4BboXew6m 6wTV1ysxi9rarBKFgU4x7ICkbMREJHIa9gFq5HzKJ84TQQdzVTwN+g9YkfeiyVOmNsRj elDg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=J36xzuyhBLKi0VZ+Hws5Z6a9JxZxNltccSD7cDPx5q8=; b=AVVi4NlS7Fz8FhXgWxvMIWgDiMhyusqZ9sq5YaaG7SufbakWNz+zUVr1mQV0xzbjvU JDLhBT0EbaP18in2cxaNCgTOGT9Jdc618TdU7lBg7ZhOEPkK+wGGiTUczBkA4AQwFe9/ gUJ5IgNXxhCeS6ZPClgLnemvUe7ZZi6uJ1rKiTkJ8VexOzeRDbEQ076lx3rdTlUqvRim C++VERYDD845evHAWOWZ7tJKh70SpuyHiCAPzl9GALpZLiIzlw3MPJyRm9tODwGhY64Y oyTGqcG61cc6jvkHh5H/jYp36gXRzeaCn6fnSKlBlAedElxVAfap+h7IZGNZg9IyPEgO pi+w== X-Gm-Message-State: ACrzQf02RtvLzQ6KfiRDzPnck5M6CJknAZN1ZV91Pyqeu2I993EoXVIf tmg7+NrtnItUsFg1mWHoBY9w8A== X-Google-Smtp-Source: AMsMyM6UHH+zo/yWaqcifKSmc1/2QM7ruLSkB8JzfvTPkTYx+Yfy6GnpprTd7yud5cEguvWmwng11Q== X-Received: by 2002:a17:902:8685:b0:186:cb66:d77b with SMTP id g5-20020a170902868500b00186cb66d77bmr5743741plo.39.1667069381087; Sat, 29 Oct 2022 11:49:41 -0700 (PDT) Received: from localhost.localdomain ([76.242.116.65]) by smtp.gmail.com with ESMTPSA id gb7-20020a17090b060700b0020d9306e735sm1377566pjb.20.2022.10.29.11.49.38 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 29 Oct 2022 11:49:40 -0700 (PDT) From: Roderick Colenbrander X-Google-Original-From: Roderick Colenbrander To: Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, Roderick Colenbrander Subject: [PATCH 11/13] HID: playstation: add DualShock4 bluetooth support. Date: Sat, 29 Oct 2022 11:48:49 -0700 Message-Id: <20221029184851.282366-12-roderick.colenbrander@sony.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221029184851.282366-1-roderick.colenbrander@sony.com> References: <20221029184851.282366-1-roderick.colenbrander@sony.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org Add support for DualShock4 in Bluetooth mode. In Bluetooth, the device is a bit strange in that after 'calibration' it switches sending all its input data from a basic report (only containing buttons/sticks) to an extended report, which also contains touchpad, motion sensors and other data. The overall design of this code is similar to the DualSense code. Signed-off-by: Roderick Colenbrander --- drivers/hid/hid-playstation.c | 170 ++++++++++++++++++++++++++++------ 1 file changed, 144 insertions(+), 26 deletions(-) diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c index 81a12aafed17..a2f1bd1400a2 100644 --- a/drivers/hid/hid-playstation.c +++ b/drivers/hid/hid-playstation.c @@ -287,11 +287,17 @@ struct dualsense_output_report { #define DS4_INPUT_REPORT_USB 0x01 #define DS4_INPUT_REPORT_USB_SIZE 64 +#define DS4_INPUT_REPORT_BT 0x11 +#define DS4_INPUT_REPORT_BT_SIZE 78 #define DS4_OUTPUT_REPORT_USB 0x05 #define DS4_OUTPUT_REPORT_USB_SIZE 32 +#define DS4_OUTPUT_REPORT_BT 0x11 +#define DS4_OUTPUT_REPORT_BT_SIZE 78 #define DS4_FEATURE_REPORT_CALIBRATION 0x02 #define DS4_FEATURE_REPORT_CALIBRATION_SIZE 37 +#define DS4_FEATURE_REPORT_CALIBRATION_BT 0x05 +#define DS4_FEATURE_REPORT_CALIBRATION_BT_SIZE 41 #define DS4_FEATURE_REPORT_FIRMWARE_INFO 0xa3 #define DS4_FEATURE_REPORT_FIRMWARE_INFO_SIZE 49 #define DS4_FEATURE_REPORT_PAIRING_INFO 0x12 @@ -310,6 +316,9 @@ struct dualsense_output_report { /* Battery status within batery_status field. */ #define DS4_BATTERY_STATUS_FULL 11 +#define DS4_OUTPUT_HWCTL_CRC32 0x40 +#define DS4_OUTPUT_HWCTL_HID 0x80 + /* Flags for DualShock4 output report. */ #define DS4_OUTPUT_VALID_FLAG0_MOTOR 0x01 #define DS4_OUTPUT_VALID_FLAG0_LED 0x02 @@ -401,6 +410,17 @@ struct dualshock4_input_report_usb { } __packed; static_assert(sizeof(struct dualshock4_input_report_usb) == DS4_INPUT_REPORT_USB_SIZE); +struct dualshock4_input_report_bt { + uint8_t report_id; /* 0x11 */ + uint8_t reserved[2]; + struct dualshock4_input_report_common common; + uint8_t num_touch_reports; + struct dualshock4_touch_report touch_reports[4]; /* BT has 4 compared to 3 for USB */ + uint8_t reserved2[2]; + __le32 crc32; +} __packed; +static_assert(sizeof(struct dualshock4_input_report_bt) == DS4_INPUT_REPORT_BT_SIZE); + /* Common data between Bluetooth and USB DualShock4 output reports. */ struct dualshock4_output_report_common { uint8_t valid_flag0; @@ -425,6 +445,16 @@ struct dualshock4_output_report_usb { } __packed; static_assert(sizeof(struct dualshock4_output_report_usb) == DS4_OUTPUT_REPORT_USB_SIZE); +struct dualshock4_output_report_bt { + uint8_t report_id; /* 0x11 */ + uint8_t hw_control; + uint8_t audio_control; + struct dualshock4_output_report_common common; + uint8_t reserved[61]; + __le32 crc32; +} __packed; +static_assert(sizeof(struct dualshock4_output_report_bt) == DS4_OUTPUT_REPORT_BT_SIZE); + /* * The DualShock4 has a main output report used to control most features. It is * largely the same between Bluetooth and USB except for different headers and CRC. @@ -434,6 +464,8 @@ struct dualshock4_output_report { uint8_t *data; /* Start of data */ uint8_t len; /* Size of output report */ + /* Points to Bluetooth data payload in case for a Bluetooth report else NULL. */ + struct dualshock4_output_report_bt *bt; /* Points to USB data payload in case for a USB report else NULL. */ struct dualshock4_output_report_usb *usb; /* Points to common section of report, so past any headers. */ @@ -1646,26 +1678,49 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4) int ret = 0; uint8_t *buf; - buf = kzalloc(DS4_FEATURE_REPORT_CALIBRATION_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; + if (ds4->base.hdev->bus == BUS_USB) { + buf = kzalloc(DS4_FEATURE_REPORT_CALIBRATION_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; - ret = ps_get_report(hdev, DS4_FEATURE_REPORT_CALIBRATION, buf, - DS4_FEATURE_REPORT_CALIBRATION_SIZE, true); - if (ret) { - hid_err(hdev, "Failed to retrieve DualShock4 calibration info: %d\n", ret); - goto err_free; + ret = ps_get_report(hdev, DS4_FEATURE_REPORT_CALIBRATION, buf, + DS4_FEATURE_REPORT_CALIBRATION_SIZE, true); + if (ret) { + hid_err(hdev, "Failed to retrieve DualShock4 calibration info: %d\n", ret); + goto err_free; + } + } else { /* Bluetooth */ + buf = kzalloc(DS4_FEATURE_REPORT_CALIBRATION_BT_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = ps_get_report(hdev, DS4_FEATURE_REPORT_CALIBRATION_BT, buf, + DS4_FEATURE_REPORT_CALIBRATION_BT_SIZE, true); + if (ret) { + hid_err(hdev, "Failed to retrieve DualShock4 calibration info: %d\n", ret); + goto err_free; + } } gyro_pitch_bias = get_unaligned_le16(&buf[1]); gyro_yaw_bias = get_unaligned_le16(&buf[3]); gyro_roll_bias = get_unaligned_le16(&buf[5]); - gyro_pitch_plus = get_unaligned_le16(&buf[7]); - gyro_pitch_minus = get_unaligned_le16(&buf[9]); - gyro_yaw_plus = get_unaligned_le16(&buf[11]); - gyro_yaw_minus = get_unaligned_le16(&buf[13]); - gyro_roll_plus = get_unaligned_le16(&buf[15]); - gyro_roll_minus = get_unaligned_le16(&buf[17]); + if (ds4->base.hdev->bus == BUS_USB) { + gyro_pitch_plus = get_unaligned_le16(&buf[7]); + gyro_pitch_minus = get_unaligned_le16(&buf[9]); + gyro_yaw_plus = get_unaligned_le16(&buf[11]); + gyro_yaw_minus = get_unaligned_le16(&buf[13]); + gyro_roll_plus = get_unaligned_le16(&buf[15]); + gyro_roll_minus = get_unaligned_le16(&buf[17]); + } else { + /* BT + Dongle */ + gyro_pitch_plus = get_unaligned_le16(&buf[7]); + gyro_yaw_plus = get_unaligned_le16(&buf[9]); + gyro_roll_plus = get_unaligned_le16(&buf[11]); + gyro_pitch_minus = get_unaligned_le16(&buf[13]); + gyro_yaw_minus = get_unaligned_le16(&buf[15]); + gyro_roll_minus = get_unaligned_le16(&buf[17]); + } gyro_speed_plus = get_unaligned_le16(&buf[19]); gyro_speed_minus = get_unaligned_le16(&buf[21]); acc_x_plus = get_unaligned_le16(&buf[23]); @@ -1731,8 +1786,11 @@ static int dualshock4_get_firmware_info(struct dualshock4 *ds4) if (!buf) return -ENOMEM; + /* Note USB and BT support the same feature report, but this report + * lacks CRC support, so must be disabled in ps_get_report. + */ ret = ps_get_report(ds4->base.hdev, DS4_FEATURE_REPORT_FIRMWARE_INFO, buf, - DS4_FEATURE_REPORT_FIRMWARE_INFO_SIZE, true); + DS4_FEATURE_REPORT_FIRMWARE_INFO_SIZE, false); if (ret) { hid_err(ds4->base.hdev, "Failed to retrieve DualShock4 firmware info: %d\n", ret); goto err_free; @@ -1748,21 +1806,38 @@ static int dualshock4_get_firmware_info(struct dualshock4 *ds4) static int dualshock4_get_mac_address(struct dualshock4 *ds4) { + struct hid_device *hdev = ds4->base.hdev; uint8_t *buf; int ret = 0; - buf = kzalloc(DS4_FEATURE_REPORT_PAIRING_INFO_SIZE, GFP_KERNEL); - if (!buf) - return -ENOMEM; + if (hdev->bus == BUS_USB) { + buf = kzalloc(DS4_FEATURE_REPORT_PAIRING_INFO_SIZE, GFP_KERNEL); + if (!buf) + return -ENOMEM; + + ret = ps_get_report(hdev, DS4_FEATURE_REPORT_PAIRING_INFO, buf, + DS4_FEATURE_REPORT_PAIRING_INFO_SIZE, false); + if (ret) { + hid_err(hdev, "Failed to retrieve DualShock4 pairing info: %d\n", ret); + goto err_free; + } - ret = ps_get_report(ds4->base.hdev, DS4_FEATURE_REPORT_PAIRING_INFO, buf, - DS4_FEATURE_REPORT_PAIRING_INFO_SIZE, true); - if (ret) { - hid_err(ds4->base.hdev, "Failed to retrieve DualShock4 pairing info: %d\n", ret); - goto err_free; - } + memcpy(ds4->base.mac_address, &buf[1], sizeof(ds4->base.mac_address)); + } else { + /* Rely on HIDP for Bluetooth */ + if (strlen(hdev->uniq) != 17) + return -EINVAL; + + ret = sscanf(hdev->uniq, "%02hhx:%02hhx:%02hhx:%02hhx:%02hhx:%02hhx", + &ds4->base.mac_address[5], &ds4->base.mac_address[4], + &ds4->base.mac_address[3], &ds4->base.mac_address[2], + &ds4->base.mac_address[1], &ds4->base.mac_address[0]); - memcpy(ds4->base.mac_address, &buf[1], sizeof(ds4->base.mac_address)); + if (ret != sizeof(ds4->base.mac_address)) + return -EINVAL; + + ret = 0; + } err_free: kfree(buf); @@ -1859,7 +1934,18 @@ static void dualshock4_init_output_report(struct dualshock4 *ds4, { struct hid_device *hdev = ds4->base.hdev; - if (hdev->bus == BUS_USB) { + if (hdev->bus == BUS_BLUETOOTH) { + struct dualshock4_output_report_bt *bt = buf; + + memset(bt, 0, sizeof(*bt)); + bt->report_id = DS4_OUTPUT_REPORT_BT; + + rp->data = buf; + rp->len = sizeof(*bt); + rp->bt = bt; + rp->usb = NULL; + rp->common = &bt->common; + } else { /* USB */ struct dualshock4_output_report_usb *usb = buf; memset(usb, 0, sizeof(*usb)); @@ -1867,6 +1953,7 @@ static void dualshock4_init_output_report(struct dualshock4 *ds4, rp->data = buf; rp->len = sizeof(*usb); + rp->bt = NULL; rp->usb = usb; rp->common = &usb->common; } @@ -1913,6 +2000,22 @@ static void dualshock4_output_worker(struct work_struct *work) spin_unlock_irqrestore(&ds4->base.lock, flags); + /* Bluetooth packets need additional flags as well as a CRC in the last 4 bytes. */ + if (report.bt) { + uint32_t crc; + uint8_t seed = PS_OUTPUT_CRC32_SEED; + + /* Hardware control flags need to set to let the device know + * there is HID data as well as CRC. + */ + report.bt->hw_control = DS4_OUTPUT_HWCTL_HID | DS4_OUTPUT_HWCTL_CRC32; + + crc = crc32_le(0xFFFFFFFF, &seed, 1); + crc = ~crc32_le(crc, report.data, report.len - 4); + + report.bt->crc32 = cpu_to_le32(crc); + } + hid_hw_output_report(ds4->base.hdev, report.data, report.len); } @@ -1940,6 +2043,19 @@ static int dualshock4_parse_report(struct ps_device *ps_dev, struct hid_report * ds4_report = &usb->common; num_touch_reports = usb->num_touch_reports; touch_reports = usb->touch_reports; + } else if (hdev->bus == BUS_BLUETOOTH && report->id == DS4_INPUT_REPORT_BT && + size == DS4_INPUT_REPORT_BT_SIZE) { + struct dualshock4_input_report_bt *bt = (struct dualshock4_input_report_bt *)data; + + /* Last 4 bytes of input report contains CRC. */ + if (!ps_check_crc32(PS_INPUT_CRC32_SEED, data, size - 4, bt->crc32)) { + hid_err(hdev, "DualShock4 input CRC's check failed\n"); + return -EILSEQ; + } + + ds4_report = &bt->common; + num_touch_reports = bt->num_touch_reports; + touch_reports = bt->touch_reports; } else { hid_err(hdev, "Unhandled reportID=%d\n", report->id); return -1; @@ -2354,7 +2470,9 @@ static void ps_remove(struct hid_device *hdev) static const struct hid_device_id ps_devices[] = { /* Sony DualShock 4 controllers for PS4 */ + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2) }, /* Sony DualSense controllers for PS5 */ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS5_CONTROLLER) }, From patchwork Sat Oct 29 18:48:50 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roderick Colenbrander X-Patchwork-Id: 620132 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 4ED91C38A02 for ; Sat, 29 Oct 2022 18:49:49 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229635AbiJ2Sts (ORCPT ); Sat, 29 Oct 2022 14:49:48 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49172 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229714AbiJ2Stq (ORCPT ); Sat, 29 Oct 2022 14:49:46 -0400 Received: from mail-pj1-x1033.google.com (mail-pj1-x1033.google.com [IPv6:2607:f8b0:4864:20::1033]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 4605F13D73 for ; Sat, 29 Oct 2022 11:49:45 -0700 (PDT) Received: by mail-pj1-x1033.google.com with SMTP id c15-20020a17090a1d0f00b0021365864446so7145149pjd.4 for ; Sat, 29 Oct 2022 11:49:45 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gaikai-com.20210112.gappssmtp.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=fsX5392UKnLgBJUW01iOJFDlpotAC8FaXD98a4yhStI=; b=djyfe5h2bzCwo7zUhkANoSN2Ruza0iMPxy90HgZRCG13oU8rLjGW0x3N3i2fC6izJS azuWBTJdW+ns35kLHXEcSu7HtUvhCVOz14frC7Fk45bUryDIrnWMF4VwZxrnV9qgBySR /ld7PXAoW21IKr96Az7pxHjIOQDw5q3xn/JoUra9fBFYwb74vMijef1VDgPrdtv4qkYB LQrzW2wTK/4nX3GU2PTvNDuDI217b2C7RF2bdc+07Mujbv13P38BAUQ7S1CrzmX8d3uu H49RztUbj6RLtC2paWbPdqjwXDFyE3x+HMcPgwxagUKIuyTYZlqGBpxVWaZSvJehiRnl QTlg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=fsX5392UKnLgBJUW01iOJFDlpotAC8FaXD98a4yhStI=; b=GI3CipBr3vJGVMa3yWfLB2I+PNbfFcg77uR5LfIPg5fTsBtQvEsuWSK+zRnxJJV/F5 KGpHipHsAqluSVxVOVIn8KxqmpGSb2XSdOzT1dZbRgYAEel0h+Z0KK+RMEc8GF3U8zCQ 5CuRG8pq7zQ7YilJH2N8o+Zr3hNAZAjftgM+Lu3rLD9bU6QuMcsTzYF2kijZNbmHM7jm RWL9hwIDzfrncCvUZ7Qb0T8n27kGiTUqLHjwgEzZVWYjdDOt+cJudbNmFy6ww8keQutz Ku4a/puCWbvQMntegpxCBzAaQDt498O4r61ZtH0REJtIFULWEOdLaQPsBEmXuFcmU58t HcsQ== X-Gm-Message-State: ACrzQf3UIzFXcixrpSg0EkJrGiRIuiCiCCzNGxjFq0CLqW6NauBJKc0R TXrB/T+V7++Roir5JAUxspD4uFw99tg2+rKq X-Google-Smtp-Source: AMsMyM7/ExqCztRIVEWaZ+3m9CgXkw2nIMtGGgV0IF/X6YKTTZe0loo/3kv1uHq3EfwYKkARnvgHyA== X-Received: by 2002:a17:90a:ce89:b0:213:167c:81e1 with SMTP id g9-20020a17090ace8900b00213167c81e1mr23396076pju.38.1667069384780; Sat, 29 Oct 2022 11:49:44 -0700 (PDT) Received: from localhost.localdomain ([76.242.116.65]) by smtp.gmail.com with ESMTPSA id gb7-20020a17090b060700b0020d9306e735sm1377566pjb.20.2022.10.29.11.49.42 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 29 Oct 2022 11:49:44 -0700 (PDT) From: Roderick Colenbrander X-Google-Original-From: Roderick Colenbrander To: Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, Roderick Colenbrander Subject: [PATCH 12/13] HID: playstation: set default DualShock4 BT poll interval to 4ms. Date: Sat, 29 Oct 2022 11:48:50 -0700 Message-Id: <20221029184851.282366-13-roderick.colenbrander@sony.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221029184851.282366-1-roderick.colenbrander@sony.com> References: <20221029184851.282366-1-roderick.colenbrander@sony.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org The poll interval for DualShock4 in Bluetooth mode is adjustable through the main output report. Configure it to 4ms, which is similar to USB. Signed-off-by: Roderick Colenbrander --- drivers/hid/hid-playstation.c | 29 +++++++++++++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c index a2f1bd1400a2..05553d07cb1b 100644 --- a/drivers/hid/hid-playstation.c +++ b/drivers/hid/hid-playstation.c @@ -316,6 +316,17 @@ struct dualsense_output_report { /* Battery status within batery_status field. */ #define DS4_BATTERY_STATUS_FULL 11 +/* The lower 6 bits of hw_control of the Bluetooth main output report + * control the interval at which Dualshock 4 reports data: + * 0x00 - 1ms + * 0x01 - 1ms + * 0x02 - 2ms + * 0x3E - 62ms + * 0x3F - disabled + */ +#define DS4_OUTPUT_HWCTL_BT_POLL_MASK 0x3F +/* Default to 4ms poll interval, which is same as USB (not adjustable). */ +#define DS4_BT_DEFAULT_POLL_INTERVAL_MS 4 #define DS4_OUTPUT_HWCTL_CRC32 0x40 #define DS4_OUTPUT_HWCTL_HID 0x80 @@ -348,6 +359,10 @@ struct dualshock4 { uint32_t prev_sensor_timestamp; uint32_t sensor_timestamp_us; + /* Bluetooth poll interval */ + bool update_bt_poll_interval; + uint8_t bt_poll_interval; + bool update_rumble; uint8_t motor_left; uint8_t motor_right; @@ -2010,6 +2025,11 @@ static void dualshock4_output_worker(struct work_struct *work) */ report.bt->hw_control = DS4_OUTPUT_HWCTL_HID | DS4_OUTPUT_HWCTL_CRC32; + if (ds4->update_bt_poll_interval) { + report.bt->hw_control |= ds4->bt_poll_interval; + ds4->update_bt_poll_interval = false; + } + crc = crc32_le(0xFFFFFFFF, &seed, 1); crc = ~crc32_le(crc, report.data, report.len - 4); @@ -2241,6 +2261,13 @@ static inline void dualshock4_schedule_work(struct dualshock4 *ds4) spin_unlock_irqrestore(&ds4->base.lock, flags); } +static void dualshock4_set_bt_poll_interval(struct dualshock4 *ds4, uint8_t interval) +{ + ds4->bt_poll_interval = interval; + ds4->update_bt_poll_interval = true; + dualshock4_schedule_work(ds4); +} + /* Set default lightbar color based on player. */ static void dualshock4_set_default_lightbar_colors(struct dualshock4 *ds4) { @@ -2372,6 +2399,8 @@ static struct ps_device *dualshock4_create(struct hid_device *hdev) goto err; } + dualshock4_set_bt_poll_interval(ds4, DS4_BT_DEFAULT_POLL_INTERVAL_MS); + ret = ps_device_set_player_id(ps_dev); if (ret) { hid_err(hdev, "Failed to assign player id for DualShock4: %d\n", ret); From patchwork Sat Oct 29 18:48:51 2022 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Roderick Colenbrander X-Patchwork-Id: 619930 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by smtp.lore.kernel.org (Postfix) with ESMTP id 721B4C433FE for ; Sat, 29 Oct 2022 18:49:52 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S229714AbiJ2Stv (ORCPT ); Sat, 29 Oct 2022 14:49:51 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:49372 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S229721AbiJ2Stt (ORCPT ); Sat, 29 Oct 2022 14:49:49 -0400 Received: from mail-pl1-x62b.google.com (mail-pl1-x62b.google.com [IPv6:2607:f8b0:4864:20::62b]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id CD04314097 for ; Sat, 29 Oct 2022 11:49:47 -0700 (PDT) Received: by mail-pl1-x62b.google.com with SMTP id u6so7517366plq.12 for ; Sat, 29 Oct 2022 11:49:47 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gaikai-com.20210112.gappssmtp.com; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to; bh=2KyW0lSBWeurKf3smir9O6Xb+3JTUvpA5yWPhcDZubs=; b=X29xN7vnc1pTXylGiSWVGriT5LBy2mMcddkenghjkYaWmLrVdo5evTKrkOJGRn1y2e I3+7J3QJoDuNs2fv4zmGSI/h2+7uekEkSVvcpF6LWTmdqMDswIboIpUmgOoxxMbK14Gg sORuIS1tXuvo+520q7i4ONwWwpOIWQa3PRGpG0z5STwn62vYb0g4IND6APRLbwKXn2LB sJwnwnBdEwfwPoe9owOBQSM5efauZXNaK/VvugT3RRk15uOjobNwhMC27rn3YQauBfxt jL4HGanhggfRpx9XKnegQV/KtleUvHf5o5Dibxle/oJvltQtLcVTWlnHBfpCfGg5yyoN PfWQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20210112; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-message-state:from:to:cc :subject:date:message-id:reply-to; bh=2KyW0lSBWeurKf3smir9O6Xb+3JTUvpA5yWPhcDZubs=; b=7816M/jmMhGy+Z02jgzZP+2S1uAwdWkL5OnAaowua/5DmLAQ+b7cLLK5DIq8KJQCmK JlgyU2wLDe96Wy7Z55SYFbO/yUU3f4+PSMpANQ3ZqQL8+xNT4Yl7xtGck/XxYUvnrCN+ 8iVQS2YWkgoQo5sPfC/GCnIfKwTCadRiMxl2SuDg95kuaD4/f4k5fY928WijM03fyuYb JlDFhcQDYsHu7DTJ3Zcg5OBXQmgnnWCbRQxXcDICq4xNZ12bF5T6g8ZaxZMkW3xr40q3 CoFM/PCxHpDjuQ0ssCVw91IOWBkeAC5P0XIFsj8L9QI6etY+Vhp/ZwCeH0xoYlezdmTH QbSQ== X-Gm-Message-State: ACrzQf1tqinMxIxCN4njugP3OcOatOtbaKdUSbELNtIS3K91A+yttiR7 1iG3ZYAkvY+/aDnc20LdFF9gmA== X-Google-Smtp-Source: AMsMyM5+g8A0kYDVse1FO0RTkYgpiwq1DtH+Yioz3AoF6QPBs/UWJD+L6sjO/PU8vNTDqU9yfBdycQ== X-Received: by 2002:a17:902:8c81:b0:178:1701:cd with SMTP id t1-20020a1709028c8100b00178170100cdmr5812561plo.138.1667069387276; Sat, 29 Oct 2022 11:49:47 -0700 (PDT) Received: from localhost.localdomain ([76.242.116.65]) by smtp.gmail.com with ESMTPSA id gb7-20020a17090b060700b0020d9306e735sm1377566pjb.20.2022.10.29.11.49.45 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 29 Oct 2022 11:49:46 -0700 (PDT) From: Roderick Colenbrander X-Google-Original-From: Roderick Colenbrander To: Jiri Kosina , Benjamin Tissoires Cc: linux-input@vger.kernel.org, Roderick Colenbrander Subject: [PATCH 13/13] HID: playstation: add DualShock4 dongle support. Date: Sat, 29 Oct 2022 11:48:51 -0700 Message-Id: <20221029184851.282366-14-roderick.colenbrander@sony.com> X-Mailer: git-send-email 2.37.3 In-Reply-To: <20221029184851.282366-1-roderick.colenbrander@sony.com> References: <20221029184851.282366-1-roderick.colenbrander@sony.com> MIME-Version: 1.0 Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org This patch adds support for the DualShock4 dongle in a very similar way we contributed to hid-sony before. The dongle is a USB to Bluetooth bridge and uses the same HID reports as a USB device. It reports data through the DS4's main USB input report independent on whether a Bluetooth controller is connected. For this reason there is custom dongle report parsing code to detect controller hotplug and kick of calibration work until we are ready to process actual input reports. The logic also incorporates a workaround needed for Steam in which hid-playstation and Steam using hidraw can fight. Signed-off-by: Roderick Colenbrander --- drivers/hid/hid-playstation.c | 146 ++++++++++++++++++++++++++++++++-- 1 file changed, 140 insertions(+), 6 deletions(-) diff --git a/drivers/hid/hid-playstation.c b/drivers/hid/hid-playstation.c index 05553d07cb1b..bae3e712a562 100644 --- a/drivers/hid/hid-playstation.c +++ b/drivers/hid/hid-playstation.c @@ -315,6 +315,11 @@ struct dualsense_output_report { #define DS4_STATUS0_CABLE_STATE BIT(4) /* Battery status within batery_status field. */ #define DS4_BATTERY_STATUS_FULL 11 +/* Status1 bit2 contains dongle connection state: + * 0 = connectd + * 1 = disconnected + */ +#define DS4_STATUS1_DONGLE_STATE BIT(2) /* The lower 6 bits of hw_control of the Bluetooth main output report * control the interval at which Dualshock 4 reports data: @@ -344,6 +349,13 @@ struct dualsense_output_report { #define DS4_TOUCHPAD_WIDTH 1920 #define DS4_TOUCHPAD_HEIGHT 942 +enum dualshock4_dongle_state { + DONGLE_DISCONNECTED, + DONGLE_CALIBRATING, + DONGLE_CONNECTED, + DONGLE_DISABLED +}; + struct dualshock4 { struct ps_device base; struct input_dev *gamepad; @@ -354,6 +366,11 @@ struct dualshock4 { struct ps_calibration_data accel_calib_data[3]; struct ps_calibration_data gyro_calib_data[3]; + /* Only used on dongle to track state transitions. */ + enum dualshock4_dongle_state dongle_state; + /* Used during calibration. */ + struct work_struct dongle_hotplug_worker; + /* Timestamp for sensor data */ bool sensor_timestamp_initialized; uint32_t prev_sensor_timestamp; @@ -513,9 +530,11 @@ static const struct {int x; int y; } ps_gamepad_hat_mapping[] = { {0, 0}, }; +static int dualshock4_get_calibration_data(struct dualshock4 *ds4); static inline void dualsense_schedule_work(struct dualsense *ds); static inline void dualshock4_schedule_work(struct dualshock4 *ds4); static void dualsense_set_lightbar(struct dualsense *ds, uint8_t red, uint8_t green, uint8_t blue); +static void dualshock4_set_default_lightbar_colors(struct dualshock4 *ds4); /* * Add a new ps_device to ps_devices if it doesn't exist. @@ -1678,6 +1697,33 @@ static struct ps_device *dualsense_create(struct hid_device *hdev) return ERR_PTR(ret); } +static void dualshock4_dongle_calibration_work(struct work_struct *work) +{ + struct dualshock4 *ds4 = container_of(work, struct dualshock4, dongle_hotplug_worker); + unsigned long flags; + enum dualshock4_dongle_state dongle_state; + int ret; + + ret = dualshock4_get_calibration_data(ds4); + 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(ds4->base.hdev, "DualShock 4 USB dongle: calibration failed, disabling device\n"); + dongle_state = DONGLE_DISABLED; + } else { + hid_info(ds4->base.hdev, "DualShock 4 USB dongle: calibration completed\n"); + dongle_state = DONGLE_CONNECTED; + } + + spin_lock_irqsave(&ds4->base.lock, flags); + ds4->dongle_state = dongle_state; + spin_unlock_irqrestore(&ds4->base.lock, flags); +} + static int dualshock4_get_calibration_data(struct dualshock4 *ds4) { struct hid_device *hdev = ds4->base.hdev; @@ -1694,15 +1740,34 @@ static int dualshock4_get_calibration_data(struct dualshock4 *ds4) uint8_t *buf; if (ds4->base.hdev->bus == BUS_USB) { + int retries; + buf = kzalloc(DS4_FEATURE_REPORT_CALIBRATION_SIZE, GFP_KERNEL); if (!buf) return -ENOMEM; - ret = ps_get_report(hdev, DS4_FEATURE_REPORT_CALIBRATION, buf, - DS4_FEATURE_REPORT_CALIBRATION_SIZE, true); - if (ret) { - hid_err(hdev, "Failed to retrieve DualShock4 calibration info: %d\n", ret); - goto err_free; + /* We should normally receive the feature report data we asked + * for, but hidraw applications such as Steam can issue feature + * reports as well. In particular for Dongle reconnects, Steam + * and this function are competing resulting in often receiving + * data for a different HID report, so retry a few times. + */ + for (retries = 0; retries < 3; retries++) { + ret = ps_get_report(hdev, DS4_FEATURE_REPORT_CALIBRATION, buf, + DS4_FEATURE_REPORT_CALIBRATION_SIZE, true); + if (ret) { + if (retries < 2) { + hid_warn(hdev, "Retrying DualShock 4 get calibration report (0x02) request\n"); + continue; + } else { + ret = -EILSEQ; + goto err_free; + } + hid_err(hdev, "Failed to retrieve DualShock4 calibration info: %d\n", ret); + goto err_free; + } else { + break; + } } } else { /* Bluetooth */ buf = kzalloc(DS4_FEATURE_REPORT_CALIBRATION_BT_SIZE, GFP_KERNEL); @@ -2220,6 +2285,62 @@ static int dualshock4_parse_report(struct ps_device *ps_dev, struct hid_report * return 0; } +static int dualshock4_dongle_parse_report(struct ps_device *ps_dev, struct hid_report *report, + u8 *data, int size) +{ + struct dualshock4 *ds4 = container_of(ps_dev, struct dualshock4, base); + bool connected = false; + + /* The dongle reports data using the main USB report (0x1) no matter whether a controller + * is connected with mostly zeros. The report does contain dongle status, which we use to + * determine if a controller is connected and if so we forward to the regular DualShock4 + * parsing code. + */ + if (data[0] == DS4_INPUT_REPORT_USB && size == DS4_INPUT_REPORT_USB_SIZE) { + struct dualshock4_input_report_common *ds4_report = (struct dualshock4_input_report_common *)&data[1]; + unsigned long flags; + + connected = ds4_report->status[1] & DS4_STATUS1_DONGLE_STATE ? false : true; + + if (ds4->dongle_state == DONGLE_DISCONNECTED && connected) { + hid_info(ps_dev->hdev, "DualShock 4 USB dongle: controller connected\n"); + + dualshock4_set_default_lightbar_colors(ds4); + + spin_lock_irqsave(&ps_dev->lock, flags); + ds4->dongle_state = DONGLE_CALIBRATING; + spin_unlock_irqrestore(&ps_dev->lock, flags); + + schedule_work(&ds4->dongle_hotplug_worker); + + /* Don't process the report since we don't have + * calibration data, but let hidraw have it anyway. + */ + return 0; + } else if ((ds4->dongle_state == DONGLE_CONNECTED || + ds4->dongle_state == DONGLE_DISABLED) && !connected) { + hid_info(ps_dev->hdev, "DualShock 4 USB dongle: controller disconnected\n"); + + spin_lock_irqsave(&ps_dev->lock, flags); + ds4->dongle_state = DONGLE_DISCONNECTED; + spin_unlock_irqrestore(&ps_dev->lock, flags); + + /* Return 0, so hidraw can get the report. */ + return 0; + } else if (ds4->dongle_state == DONGLE_CALIBRATING || + ds4->dongle_state == DONGLE_DISABLED || + ds4->dongle_state == DONGLE_DISCONNECTED) { + /* Return 0, so hidraw can get the report. */ + return 0; + } + } + + if (connected) + return dualshock4_parse_report(ps_dev, report, data, size); + + return 0; +} + static int dualshock4_play_effect(struct input_dev *dev, void *data, struct ff_effect *effect) { struct hid_device *hdev = input_get_drvdata(dev); @@ -2249,6 +2370,9 @@ static void dualshock4_remove(struct ps_device *ps_dev) spin_unlock_irqrestore(&ds4->base.lock, flags); cancel_work_sync(&ds4->output_worker); + + if (ps_dev->hdev->product == USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE) + cancel_work_sync(&ds4->dongle_hotplug_worker); } static inline void dualshock4_schedule_work(struct dualshock4 *ds4) @@ -2342,6 +2466,14 @@ static struct ps_device *dualshock4_create(struct hid_device *hdev) if (!ds4->output_report_dmabuf) return ERR_PTR(-ENOMEM); + if (hdev->product == USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE) { + ds4->dongle_state = DONGLE_DISCONNECTED; + INIT_WORK(&ds4->dongle_hotplug_worker, dualshock4_dongle_calibration_work); + + /* Override parse report for dongle specific hotplug handling. */ + ps_dev->parse_report = dualshock4_dongle_parse_report; + } + ret = dualshock4_get_mac_address(ds4); if (ret) { hid_err(hdev, "Failed to get MAC address from DualShock4\n"); @@ -2457,7 +2589,8 @@ static int ps_probe(struct hid_device *hdev, const struct hid_device_id *id) } if (hdev->product == USB_DEVICE_ID_SONY_PS4_CONTROLLER || - hdev->product == USB_DEVICE_ID_SONY_PS4_CONTROLLER_2) { + hdev->product == USB_DEVICE_ID_SONY_PS4_CONTROLLER_2 || + hdev->product == USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE) { dev = dualshock4_create(hdev); if (IS_ERR(dev)) { hid_err(hdev, "Failed to create dualshock4.\n"); @@ -2503,6 +2636,7 @@ static const struct hid_device_id ps_devices[] = { { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_2) }, + { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS4_CONTROLLER_DONGLE) }, /* Sony DualSense controllers for PS5 */ { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS5_CONTROLLER) }, { HID_USB_DEVICE(USB_VENDOR_ID_SONY, USB_DEVICE_ID_SONY_PS5_CONTROLLER) },