From patchwork Wed Nov 6 06:06:39 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Ogorchock X-Patchwork-Id: 11229339 Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 0312015AB for ; Wed, 6 Nov 2019 06:06:59 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id AC56C214D8 for ; Wed, 6 Nov 2019 06:06:58 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Mffn+Dgo" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729922AbfKFGG6 (ORCPT ); Wed, 6 Nov 2019 01:06:58 -0500 Received: from mail-yb1-f194.google.com ([209.85.219.194]:35089 "EHLO mail-yb1-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725812AbfKFGG6 (ORCPT ); Wed, 6 Nov 2019 01:06:58 -0500 Received: by mail-yb1-f194.google.com with SMTP id i12so4412615ybg.2 for ; Tue, 05 Nov 2019 22:06:57 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=ntFjVH6W3XTbCYFNYrcVT79qmHG9yrcEapKcCv4IGJI=; b=Mffn+DgoYk36ykw1v3Es4N2i5/ognJEBsgtCmC2Sz0jKwH/dw/hvihk9vORj9Kb+1K 31+jTnGL32+/2n55FidoTcDGuGOljABUi0gZEVM5Pc/+S8TFrWso+uxp9fwo/z46nLIL NTY3tqObAEdBI50eLH56G4TExvC83ElvOJVtWDEiKM1h6jY5ZICGYandn3EVzsQ1A2BK t8ukoDq5TIjOmi1VzmZwqS1c/LeQDXTKJ71ELJUqfiRY45hIbdwHD6NaLpKqK/IAB4al KzXwgNNLPPhdOf08ntMUecjnoHRy9H8TetDgfZby5ucAyWaiKjpNzu+vuUnsjT19A4ny y9qA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=ntFjVH6W3XTbCYFNYrcVT79qmHG9yrcEapKcCv4IGJI=; b=AkA3ykHxMLKhIVK9H3Mu1q2tYFHY34RX4PEdMhRcEjWpxePcy6d64UijGr73d4BTci gWn9NVKu5p7IY4pStqWYFjYbiZZRddzE+m5OAad+9TkhEMjtEIaWnY56P6omcoB1Tixj V5RnNiTH8g/z6AuiUlilH7sgJuFC+7YfgaKkSIUEgQ4YklzknuR7lDHDWq7Y5ecfo6sb rQDmtp0nWL4HD8dgXO0TXIj+wxk6ktKLSM1Lu4+uNi8RaCPXmiYrwH/lKrVtrgIpLafn g50a9jNwr4O4lZ5kGczp5EfbTswbztqfBTRuOIyCWB+YT/shA/b2ctuw6GWlSeuSArHL iMzQ== X-Gm-Message-State: APjAAAVtOyzbej773ghmyMCxRRfRUzKBiGlm7Jz80q9P8UQv+I9XMFop Zt/OcgepCPtLhr0HUncUnsBfPcTqNfI30A== X-Google-Smtp-Source: APXvYqwC8/pg7KcuAsEp+vl7HbsideW1R+tokJ9zSkSLP4ToZdC3pLesHq2dK89hu81ZL/aNHOG/rg== X-Received: by 2002:a25:ad8e:: with SMTP id z14mr773735ybi.170.1573020415944; Tue, 05 Nov 2019 22:06:55 -0800 (PST) Received: from Arrakis.djogorchock.com ([136.33.205.58]) by smtp.gmail.com with ESMTPSA id e82sm11039596ywb.87.2019.11.05.22.06.54 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 05 Nov 2019 22:06:55 -0800 (PST) From: "Daniel J. Ogorchock" To: linux-input@vger.kernel.org Cc: thunderbird2k@gmail.com, blaws05@gmail.com, benjamin.tissoires@redhat.com, jikos@kernel.org, Roderick.Colenbrander@sony.com, svv@google.com, s.jegen@gmail.com, "Daniel J. Ogorchock" Subject: [PATCH v9 01/10] HID: nintendo: add nintendo switch controller driver Date: Wed, 6 Nov 2019 00:06:39 -0600 Message-Id: <20191106060648.3018478-2-djogorchock@gmail.com> X-Mailer: git-send-email 2.23.0 In-Reply-To: <20191106060648.3018478-1-djogorchock@gmail.com> References: <20191106060648.3018478-1-djogorchock@gmail.com> MIME-Version: 1.0 Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org The hid-nintendo driver supports the Nintendo Switch Pro Controllers and the Joy-Cons. The Pro Controllers can be used over USB or Bluetooth. The Joy-Cons each create their own, independent input devices, so it is up to userspace to combine them if desired. Signed-off-by: Daniel J. Ogorchock --- MAINTAINERS | 6 + drivers/hid/Kconfig | 11 + drivers/hid/Makefile | 1 + drivers/hid/hid-ids.h | 3 + drivers/hid/hid-nintendo.c | 806 +++++++++++++++++++++++++++++++++++++ 5 files changed, 827 insertions(+) create mode 100644 drivers/hid/hid-nintendo.c diff --git a/MAINTAINERS b/MAINTAINERS index 1d235c674be8..9e9846cdd524 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -11393,6 +11393,12 @@ S: Maintained F: Documentation/scsi/NinjaSCSI.txt F: drivers/scsi/nsp32* +NINTENDO HID DRIVER +M: Daniel J. Ogorchock +L: linux-input@vger.kernel.org +S: Maintained +F: drivers/hid/hid-nintendo* + NIOS2 ARCHITECTURE M: Ley Foon Tan L: nios2-dev@lists.rocketboards.org (moderated for non-subscribers) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 3872e03d9a59..47ec750c2baf 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -692,6 +692,17 @@ config HID_MULTITOUCH To compile this driver as a module, choose M here: the module will be called hid-multitouch. +config HID_NINTENDO + tristate "Nintendo Joy-Con and Pro Controller support" + depends on HID + help + Adds support for the Nintendo Switch Joy-Cons and Pro Controller. + All controllers support bluetooth, and the Pro Controller also supports + its USB mode. + + To compile this driver as a module, choose M here: the + module will be called hid-nintendo. + config HID_NTI tristate "NTI keyboard adapters" ---help--- diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index cc5d827c9164..398d5e91fa4b 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -72,6 +72,7 @@ obj-$(CONFIG_HID_MAYFLASH) += hid-mf.o obj-$(CONFIG_HID_MICROSOFT) += hid-microsoft.o obj-$(CONFIG_HID_MONTEREY) += hid-monterey.o obj-$(CONFIG_HID_MULTITOUCH) += hid-multitouch.o +obj-$(CONFIG_HID_NINTENDO) += hid-nintendo.o obj-$(CONFIG_HID_NTI) += hid-nti.o obj-$(CONFIG_HID_NTRIG) += hid-ntrig.o obj-$(CONFIG_HID_ORTEK) += hid-ortek.o diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index e4d51ce20a6a..4a916c00b749 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -864,6 +864,9 @@ #define USB_VENDOR_ID_NINTENDO 0x057e #define USB_DEVICE_ID_NINTENDO_WIIMOTE 0x0306 #define USB_DEVICE_ID_NINTENDO_WIIMOTE2 0x0330 +#define USB_DEVICE_ID_NINTENDO_JOYCONL 0x2006 +#define USB_DEVICE_ID_NINTENDO_JOYCONR 0x2007 +#define USB_DEVICE_ID_NINTENDO_PROCON 0x2009 #define USB_VENDOR_ID_NOVATEK 0x0603 #define USB_DEVICE_ID_NOVATEK_PCT 0x0600 diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c new file mode 100644 index 000000000000..882b3758c3be --- /dev/null +++ b/drivers/hid/hid-nintendo.c @@ -0,0 +1,806 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * HID driver for Nintendo Switch Joy-Cons and Pro Controllers + * + * Copyright (c) 2019 Daniel J. Ogorchock + * + * The following resources/projects were referenced for this driver: + * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering + * https://gitlab.com/pjranki/joycon-linux-kernel (Peter Rankin) + * https://github.com/FrotBot/SwitchProConLinuxUSB + * https://github.com/MTCKC/ProconXInput + * hid-wiimote kernel hid driver + * hid-logitech-hidpp driver + * + * This driver supports the Nintendo Switch Joy-Cons and Pro Controllers. The + * Pro Controllers can either be used over USB or Bluetooth. + * + * The driver will retrieve the factory calibration info from the controllers, + * so little to no user calibration should be required. + * + */ + +#include "hid-ids.h" +#include +#include +#include +#include +#include +#include + +/* + * Reference the url below for the following HID report defines: + * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering + */ + +/* Output Reports */ +static const u8 JC_OUTPUT_RUMBLE_AND_SUBCMD = 0x01; +static const u8 JC_OUTPUT_FW_UPDATE_PKT = 0x03; +static const u8 JC_OUTPUT_RUMBLE_ONLY = 0x10; +static const u8 JC_OUTPUT_MCU_DATA = 0x11; +static const u8 JC_OUTPUT_USB_CMD = 0x80; + +/* Subcommand IDs */ +static const u8 JC_SUBCMD_STATE = 0x00; +static const u8 JC_SUBCMD_MANUAL_BT_PAIRING = 0x01; +static const u8 JC_SUBCMD_REQ_DEV_INFO = 0x02; +static const u8 JC_SUBCMD_SET_REPORT_MODE = 0x03; +static const u8 JC_SUBCMD_TRIGGERS_ELAPSED = 0x04; +static const u8 JC_SUBCMD_GET_PAGE_LIST_STATE = 0x05; +static const u8 JC_SUBCMD_SET_HCI_STATE = 0x06; +static const u8 JC_SUBCMD_RESET_PAIRING_INFO = 0x07; +static const u8 JC_SUBCMD_LOW_POWER_MODE = 0x08; +static const u8 JC_SUBCMD_SPI_FLASH_READ = 0x10; +static const u8 JC_SUBCMD_SPI_FLASH_WRITE = 0x11; +static const u8 JC_SUBCMD_RESET_MCU = 0x20; +static const u8 JC_SUBCMD_SET_MCU_CONFIG = 0x21; +static const u8 JC_SUBCMD_SET_MCU_STATE = 0x22; +static const u8 JC_SUBCMD_SET_PLAYER_LIGHTS = 0x30; +static const u8 JC_SUBCMD_GET_PLAYER_LIGHTS = 0x31; +static const u8 JC_SUBCMD_SET_HOME_LIGHT = 0x38; +static const u8 JC_SUBCMD_ENABLE_IMU = 0x40; +static const u8 JC_SUBCMD_SET_IMU_SENSITIVITY = 0x41; +static const u8 JC_SUBCMD_WRITE_IMU_REG = 0x42; +static const u8 JC_SUBCMD_READ_IMU_REG = 0x43; +static const u8 JC_SUBCMD_ENABLE_VIBRATION = 0x48; +static const u8 JC_SUBCMD_GET_REGULATED_VOLTAGE = 0x50; + +/* Input Reports */ +static const u8 JC_INPUT_BUTTON_EVENT = 0x3F; +static const u8 JC_INPUT_SUBCMD_REPLY = 0x21; +static const u8 JC_INPUT_IMU_DATA = 0x30; +static const u8 JC_INPUT_MCU_DATA = 0x31; +static const u8 JC_INPUT_USB_RESPONSE = 0x81; + +/* Feature Reports */ +static const u8 JC_FEATURE_LAST_SUBCMD = 0x02; +static const u8 JC_FEATURE_OTA_FW_UPGRADE = 0x70; +static const u8 JC_FEATURE_SETUP_MEM_READ = 0x71; +static const u8 JC_FEATURE_MEM_READ = 0x72; +static const u8 JC_FEATURE_ERASE_MEM_SECTOR = 0x73; +static const u8 JC_FEATURE_MEM_WRITE = 0x74; +static const u8 JC_FEATURE_LAUNCH = 0x75; + +/* USB Commands */ +static const u8 JC_USB_CMD_CONN_STATUS = 0x01; +static const u8 JC_USB_CMD_HANDSHAKE = 0x02; +static const u8 JC_USB_CMD_BAUDRATE_3M = 0x03; +static const u8 JC_USB_CMD_NO_TIMEOUT = 0x04; +static const u8 JC_USB_CMD_EN_TIMEOUT = 0x05; +static const u8 JC_USB_RESET = 0x06; +static const u8 JC_USB_PRE_HANDSHAKE = 0x91; +static const u8 JC_USB_SEND_UART = 0x92; + +/* SPI storage addresses of factory calibration data */ +static const u16 JC_CAL_DATA_START = 0x603d; +static const u16 JC_CAL_DATA_END = 0x604e; +#define JC_CAL_DATA_SIZE (JC_CAL_DATA_END - JC_CAL_DATA_START + 1) + + +/* The raw analog joystick values will be mapped in terms of this magnitude */ +static const u16 JC_MAX_STICK_MAG = 32767; +static const u16 JC_STICK_FUZZ = 250; +static const u16 JC_STICK_FLAT = 500; + +/* States for controller state machine */ +enum joycon_ctlr_state { + JOYCON_CTLR_STATE_INIT, + JOYCON_CTLR_STATE_READ, +}; + +struct joycon_stick_cal { + s32 max; + s32 min; + s32 center; +}; + +/* + * All the controller's button values are stored in a u32. + * They can be accessed with bitwise ANDs. + */ +static const u32 JC_BTN_Y = BIT(0); +static const u32 JC_BTN_X = BIT(1); +static const u32 JC_BTN_B = BIT(2); +static const u32 JC_BTN_A = BIT(3); +static const u32 JC_BTN_SR_R = BIT(4); +static const u32 JC_BTN_SL_R = BIT(5); +static const u32 JC_BTN_R = BIT(6); +static const u32 JC_BTN_ZR = BIT(7); +static const u32 JC_BTN_MINUS = BIT(8); +static const u32 JC_BTN_PLUS = BIT(9); +static const u32 JC_BTN_RSTICK = BIT(10); +static const u32 JC_BTN_LSTICK = BIT(11); +static const u32 JC_BTN_HOME = BIT(12); +static const u32 JC_BTN_CAP = BIT(13); /* capture button */ +static const u32 JC_BTN_DOWN = BIT(16); +static const u32 JC_BTN_UP = BIT(17); +static const u32 JC_BTN_RIGHT = BIT(18); +static const u32 JC_BTN_LEFT = BIT(19); +static const u32 JC_BTN_SR_L = BIT(20); +static const u32 JC_BTN_SL_L = BIT(21); +static const u32 JC_BTN_L = BIT(22); +static const u32 JC_BTN_ZL = BIT(23); + +enum joycon_msg_type { + JOYCON_MSG_TYPE_NONE, + JOYCON_MSG_TYPE_USB, + JOYCON_MSG_TYPE_SUBCMD, +}; + +struct joycon_subcmd_request { + u8 output_id; /* must be 0x01 for subcommand, 0x10 for rumble only */ + u8 packet_num; /* incremented every send */ + u8 rumble_data[8]; + u8 subcmd_id; + u8 data[0]; /* length depends on the subcommand */ +} __packed; + +struct joycon_subcmd_reply { + u8 ack; /* MSB 1 for ACK, 0 for NACK */ + u8 id; /* id of requested subcmd */ + u8 data[0]; /* will be at most 35 bytes */ +} __packed; + +struct joycon_input_report { + u8 id; + u8 timer; + u8 bat_con; /* battery and connection info */ + u8 button_status[3]; + u8 left_stick[3]; + u8 right_stick[3]; + u8 vibrator_report; + + /* + * If support for firmware updates, gyroscope data, and/or NFC/IR + * are added in the future, this can be swapped for a union. + */ + struct joycon_subcmd_reply reply; +} __packed; + +#define JC_MAX_RESP_SIZE (sizeof(struct joycon_input_report) + 35) + +/* Each physical controller is associated with a joycon_ctlr struct */ +struct joycon_ctlr { + struct hid_device *hdev; + struct input_dev *input; + enum joycon_ctlr_state ctlr_state; + + /* The following members are used for synchronous sends/receives */ + enum joycon_msg_type msg_type; + u8 subcmd_num; + struct mutex output_mutex; + u8 input_buf[JC_MAX_RESP_SIZE]; + wait_queue_head_t wait; + bool received_resp; + u8 usb_ack_match; + u8 subcmd_ack_match; + + /* factory calibration data */ + struct joycon_stick_cal left_stick_cal_x; + struct joycon_stick_cal left_stick_cal_y; + struct joycon_stick_cal right_stick_cal_x; + struct joycon_stick_cal right_stick_cal_y; + +}; + +static int __joycon_hid_send(struct hid_device *hdev, u8 *data, size_t len) +{ + u8 *buf; + int ret; + + buf = kmemdup(data, len, GFP_KERNEL); + if (!buf) + return -ENOMEM; + ret = hid_hw_output_report(hdev, buf, len); + kfree(buf); + if (ret < 0) + hid_dbg(hdev, "Failed to send output report ret=%d\n", ret); + return ret; +} + +static int joycon_hid_send_sync(struct joycon_ctlr *ctlr, u8 *data, size_t len) +{ + int ret; + + ret = __joycon_hid_send(ctlr->hdev, data, len); + if (ret < 0) { + memset(ctlr->input_buf, 0, JC_MAX_RESP_SIZE); + return ret; + } + + if (!wait_event_timeout(ctlr->wait, ctlr->received_resp, HZ)) { + hid_dbg(ctlr->hdev, "synchronous send/receive timed out\n"); + memset(ctlr->input_buf, 0, JC_MAX_RESP_SIZE); + return -ETIMEDOUT; + } + + ctlr->received_resp = false; + return 0; +} + +static int joycon_send_usb(struct joycon_ctlr *ctlr, u8 cmd) +{ + int ret; + u8 buf[2] = {JC_OUTPUT_USB_CMD}; + + buf[1] = cmd; + ctlr->usb_ack_match = cmd; + ctlr->msg_type = JOYCON_MSG_TYPE_USB; + ret = joycon_hid_send_sync(ctlr, buf, sizeof(buf)); + if (ret) + hid_dbg(ctlr->hdev, "send usb command failed; ret=%d\n", ret); + return ret; +} + +static int joycon_send_subcmd(struct joycon_ctlr *ctlr, + struct joycon_subcmd_request *subcmd, + size_t data_len) +{ + int ret; + + subcmd->output_id = JC_OUTPUT_RUMBLE_AND_SUBCMD; + subcmd->packet_num = ctlr->subcmd_num; + if (++ctlr->subcmd_num > 0xF) + ctlr->subcmd_num = 0; + ctlr->subcmd_ack_match = subcmd->subcmd_id; + ctlr->msg_type = JOYCON_MSG_TYPE_SUBCMD; + + ret = joycon_hid_send_sync(ctlr, (u8 *)subcmd, + sizeof(*subcmd) + data_len); + if (ret < 0) + hid_dbg(ctlr->hdev, "send subcommand failed; ret=%d\n", ret); + else + ret = 0; + return ret; +} + +/* Supply nibbles for flash and on. Ones correspond to active */ +static int joycon_set_player_leds(struct joycon_ctlr *ctlr, u8 flash, u8 on) +{ + struct joycon_subcmd_request *req; + u8 buffer[sizeof(*req) + 1] = { 0 }; + + req = (struct joycon_subcmd_request *)buffer; + req->subcmd_id = JC_SUBCMD_SET_PLAYER_LIGHTS; + req->data[0] = (flash << 4) | on; + + hid_dbg(ctlr->hdev, "setting player leds\n"); + return joycon_send_subcmd(ctlr, req, 1); +} + +static const u16 DFLT_STICK_CAL_CEN = 2000; +static const u16 DFLT_STICK_CAL_MAX = 3500; +static const u16 DFLT_STICK_CAL_MIN = 500; +static int joycon_request_calibration(struct joycon_ctlr *ctlr) +{ + struct joycon_subcmd_request *req; + u8 buffer[sizeof(*req) + 5] = { 0 }; + struct joycon_input_report *report; + struct joycon_stick_cal *cal_x; + struct joycon_stick_cal *cal_y; + s32 x_max_above; + s32 x_min_below; + s32 y_max_above; + s32 y_min_below; + u8 *data; + u8 *raw_cal; + int ret; + + req = (struct joycon_subcmd_request *)buffer; + req->subcmd_id = JC_SUBCMD_SPI_FLASH_READ; + data = req->data; + data[0] = 0xFF & JC_CAL_DATA_START; + data[1] = 0xFF & (JC_CAL_DATA_START >> 8); + data[2] = 0xFF & (JC_CAL_DATA_START >> 16); + data[3] = 0xFF & (JC_CAL_DATA_START >> 24); + data[4] = JC_CAL_DATA_SIZE; + + hid_dbg(ctlr->hdev, "requesting cal data\n"); + ret = joycon_send_subcmd(ctlr, req, 5); + if (ret) { + hid_warn(ctlr->hdev, + "Failed to read stick cal, using defaults; ret=%d\n", + ret); + + ctlr->left_stick_cal_x.center = DFLT_STICK_CAL_CEN; + ctlr->left_stick_cal_x.max = DFLT_STICK_CAL_MAX; + ctlr->left_stick_cal_x.min = DFLT_STICK_CAL_MIN; + + ctlr->left_stick_cal_y.center = DFLT_STICK_CAL_CEN; + ctlr->left_stick_cal_y.max = DFLT_STICK_CAL_MAX; + ctlr->left_stick_cal_y.min = DFLT_STICK_CAL_MIN; + + ctlr->right_stick_cal_x.center = DFLT_STICK_CAL_CEN; + ctlr->right_stick_cal_x.max = DFLT_STICK_CAL_MAX; + ctlr->right_stick_cal_x.min = DFLT_STICK_CAL_MIN; + + ctlr->right_stick_cal_y.center = DFLT_STICK_CAL_CEN; + ctlr->right_stick_cal_y.max = DFLT_STICK_CAL_MAX; + ctlr->right_stick_cal_y.min = DFLT_STICK_CAL_MIN; + + return ret; + } + + report = (struct joycon_input_report *)ctlr->input_buf; + raw_cal = &report->reply.data[5]; + + /* left stick calibration parsing */ + cal_x = &ctlr->left_stick_cal_x; + cal_y = &ctlr->left_stick_cal_y; + + x_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 0), 0, 12); + y_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 1), 4, 12); + cal_x->center = hid_field_extract(ctlr->hdev, (raw_cal + 3), 0, 12); + cal_y->center = hid_field_extract(ctlr->hdev, (raw_cal + 4), 4, 12); + x_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 6), 0, 12); + y_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 7), 4, 12); + cal_x->max = cal_x->center + x_max_above; + cal_x->min = cal_x->center - x_min_below; + cal_y->max = cal_y->center + y_max_above; + cal_y->min = cal_y->center - y_min_below; + + /* right stick calibration parsing */ + raw_cal += 9; + cal_x = &ctlr->right_stick_cal_x; + cal_y = &ctlr->right_stick_cal_y; + + cal_x->center = hid_field_extract(ctlr->hdev, (raw_cal + 0), 0, 12); + cal_y->center = hid_field_extract(ctlr->hdev, (raw_cal + 1), 4, 12); + x_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 3), 0, 12); + y_min_below = hid_field_extract(ctlr->hdev, (raw_cal + 4), 4, 12); + x_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 6), 0, 12); + y_max_above = hid_field_extract(ctlr->hdev, (raw_cal + 7), 4, 12); + cal_x->max = cal_x->center + x_max_above; + cal_x->min = cal_x->center - x_min_below; + cal_y->max = cal_y->center + y_max_above; + cal_y->min = cal_y->center - y_min_below; + + hid_dbg(ctlr->hdev, "calibration:\n" + "l_x_c=%d l_x_max=%d l_x_min=%d\n" + "l_y_c=%d l_y_max=%d l_y_min=%d\n" + "r_x_c=%d r_x_max=%d r_x_min=%d\n" + "r_y_c=%d r_y_max=%d r_y_min=%d\n", + ctlr->left_stick_cal_x.center, + ctlr->left_stick_cal_x.max, + ctlr->left_stick_cal_x.min, + ctlr->left_stick_cal_y.center, + ctlr->left_stick_cal_y.max, + ctlr->left_stick_cal_y.min, + ctlr->right_stick_cal_x.center, + ctlr->right_stick_cal_x.max, + ctlr->right_stick_cal_x.min, + ctlr->right_stick_cal_y.center, + ctlr->right_stick_cal_y.max, + ctlr->right_stick_cal_y.min); + + return 0; +} + +static int joycon_set_report_mode(struct joycon_ctlr *ctlr) +{ + struct joycon_subcmd_request *req; + u8 buffer[sizeof(*req) + 1] = { 0 }; + + req = (struct joycon_subcmd_request *)buffer; + req->subcmd_id = JC_SUBCMD_SET_REPORT_MODE; + req->data[0] = 0x30; /* standard, full report mode */ + + hid_dbg(ctlr->hdev, "setting controller report mode\n"); + return joycon_send_subcmd(ctlr, req, 1); +} + +static s32 joycon_map_stick_val(struct joycon_stick_cal *cal, s32 val) +{ + s32 center = cal->center; + s32 min = cal->min; + s32 max = cal->max; + s32 new_val; + + if (val > center) { + new_val = (val - center) * JC_MAX_STICK_MAG; + new_val /= (max - center); + } else { + new_val = (center - val) * -JC_MAX_STICK_MAG; + new_val /= (center - min); + } + new_val = clamp(new_val, (s32)-JC_MAX_STICK_MAG, (s32)JC_MAX_STICK_MAG); + return new_val; +} + +static void joycon_parse_report(struct joycon_ctlr *ctlr, + struct joycon_input_report *rep) +{ + struct input_dev *dev = ctlr->input; + u32 btns; + u32 id = ctlr->hdev->product; + + btns = hid_field_extract(ctlr->hdev, rep->button_status, 0, 24); + + if (id != USB_DEVICE_ID_NINTENDO_JOYCONR) { + u16 raw_x; + u16 raw_y; + s32 x; + s32 y; + + /* get raw stick values */ + raw_x = hid_field_extract(ctlr->hdev, rep->left_stick, 0, 12); + raw_y = hid_field_extract(ctlr->hdev, + rep->left_stick + 1, 4, 12); + /* map the stick values */ + x = joycon_map_stick_val(&ctlr->left_stick_cal_x, raw_x); + y = -joycon_map_stick_val(&ctlr->left_stick_cal_y, raw_y); + /* report sticks */ + input_report_abs(dev, ABS_X, x); + input_report_abs(dev, ABS_Y, y); + + /* report buttons */ + input_report_key(dev, BTN_TL, btns & JC_BTN_L); + input_report_key(dev, BTN_TL2, btns & JC_BTN_ZL); + if (id != USB_DEVICE_ID_NINTENDO_PROCON) { + /* Report the S buttons as the non-existent triggers */ + input_report_key(dev, BTN_TR, btns & JC_BTN_SL_L); + input_report_key(dev, BTN_TR2, btns & JC_BTN_SR_L); + } + input_report_key(dev, BTN_SELECT, btns & JC_BTN_MINUS); + input_report_key(dev, BTN_THUMBL, btns & JC_BTN_LSTICK); + input_report_key(dev, BTN_Z, btns & JC_BTN_CAP); + input_report_key(dev, BTN_DPAD_DOWN, btns & JC_BTN_DOWN); + input_report_key(dev, BTN_DPAD_UP, btns & JC_BTN_UP); + input_report_key(dev, BTN_DPAD_RIGHT, btns & JC_BTN_RIGHT); + input_report_key(dev, BTN_DPAD_LEFT, btns & JC_BTN_LEFT); + } + if (id != USB_DEVICE_ID_NINTENDO_JOYCONL) { + u16 raw_x; + u16 raw_y; + s32 x; + s32 y; + + /* get raw stick values */ + raw_x = hid_field_extract(ctlr->hdev, rep->right_stick, 0, 12); + raw_y = hid_field_extract(ctlr->hdev, + rep->right_stick + 1, 4, 12); + /* map stick values */ + x = joycon_map_stick_val(&ctlr->right_stick_cal_x, raw_x); + y = -joycon_map_stick_val(&ctlr->right_stick_cal_y, raw_y); + /* report sticks */ + input_report_abs(dev, ABS_RX, x); + input_report_abs(dev, ABS_RY, y); + + /* report buttons */ + input_report_key(dev, BTN_TR, btns & JC_BTN_R); + input_report_key(dev, BTN_TR2, btns & JC_BTN_ZR); + if (id != USB_DEVICE_ID_NINTENDO_PROCON) { + /* Report the S buttons as the non-existent triggers */ + input_report_key(dev, BTN_TL, btns & JC_BTN_SL_R); + input_report_key(dev, BTN_TL2, btns & JC_BTN_SR_R); + } + input_report_key(dev, BTN_TR2, btns & JC_BTN_ZR); + input_report_key(dev, BTN_START, btns & JC_BTN_PLUS); + input_report_key(dev, BTN_THUMBR, btns & JC_BTN_RSTICK); + input_report_key(dev, BTN_MODE, btns & JC_BTN_HOME); + input_report_key(dev, BTN_WEST, btns & JC_BTN_Y); + input_report_key(dev, BTN_NORTH, btns & JC_BTN_X); + input_report_key(dev, BTN_EAST, btns & JC_BTN_A); + input_report_key(dev, BTN_SOUTH, btns & JC_BTN_B); + } + + input_sync(dev); +} + + +static const unsigned int joycon_button_inputs[] = { + BTN_SELECT, BTN_Z, BTN_THUMBL, + BTN_START, BTN_MODE, BTN_THUMBR, + BTN_SOUTH, BTN_EAST, BTN_NORTH, BTN_WEST, + BTN_DPAD_UP, BTN_DPAD_DOWN, BTN_DPAD_LEFT, BTN_DPAD_RIGHT, + BTN_TL, BTN_TR, BTN_TL2, BTN_TR2, + 0 /* 0 signals end of array */ +}; + +static DEFINE_MUTEX(joycon_input_num_mutex); +static int joycon_input_create(struct joycon_ctlr *ctlr) +{ + struct hid_device *hdev; + static int input_num = 1; + const char *name; + int ret; + int i; + + hdev = ctlr->hdev; + + switch (hdev->product) { + case USB_DEVICE_ID_NINTENDO_PROCON: + name = "Nintendo Switch Pro Controller"; + break; + case USB_DEVICE_ID_NINTENDO_JOYCONL: + name = "Nintendo Switch Left Joy-Con"; + break; + case USB_DEVICE_ID_NINTENDO_JOYCONR: + name = "Nintendo Switch Right Joy-Con"; + break; + default: /* Should be impossible */ + hid_err(hdev, "Invalid hid product\n"); + return -EINVAL; + } + + ctlr->input = devm_input_allocate_device(&hdev->dev); + if (!ctlr->input) + return -ENOMEM; + ctlr->input->id.bustype = hdev->bus; + ctlr->input->id.vendor = hdev->vendor; + ctlr->input->id.product = hdev->product; + ctlr->input->id.version = hdev->version; + ctlr->input->name = name; + input_set_drvdata(ctlr->input, ctlr); + + + /* set up sticks */ + input_set_abs_params(ctlr->input, ABS_X, + -JC_MAX_STICK_MAG, JC_MAX_STICK_MAG, + JC_STICK_FUZZ, JC_STICK_FLAT); + input_set_abs_params(ctlr->input, ABS_Y, + -JC_MAX_STICK_MAG, JC_MAX_STICK_MAG, + JC_STICK_FUZZ, JC_STICK_FLAT); + input_set_abs_params(ctlr->input, ABS_RX, + -JC_MAX_STICK_MAG, JC_MAX_STICK_MAG, + JC_STICK_FUZZ, JC_STICK_FLAT); + input_set_abs_params(ctlr->input, ABS_RY, + -JC_MAX_STICK_MAG, JC_MAX_STICK_MAG, + JC_STICK_FUZZ, JC_STICK_FLAT); + /* set up buttons */ + for (i = 0; joycon_button_inputs[i] > 0; i++) + input_set_capability(ctlr->input, EV_KEY, + joycon_button_inputs[i]); + + ret = input_register_device(ctlr->input); + if (ret) + return ret; + + /* Set the default controller player leds based on controller number */ + mutex_lock(&joycon_input_num_mutex); + mutex_lock(&ctlr->output_mutex); + ret = joycon_set_player_leds(ctlr, 0, 0xF >> (4 - input_num)); + if (ret) + hid_warn(ctlr->hdev, "Failed to set leds; ret=%d\n", ret); + mutex_unlock(&ctlr->output_mutex); + if (++input_num > 4) + input_num = 1; + mutex_unlock(&joycon_input_num_mutex); + + return 0; +} + +/* Common handler for parsing inputs */ +static int joycon_ctlr_read_handler(struct joycon_ctlr *ctlr, u8 *data, + int size) +{ + int ret = 0; + + if (data[0] == JC_INPUT_SUBCMD_REPLY || data[0] == JC_INPUT_IMU_DATA || + data[0] == JC_INPUT_MCU_DATA) { + if (size >= 12) /* make sure it contains the input report */ + joycon_parse_report(ctlr, + (struct joycon_input_report *)data); + } + + return ret; +} + +static int joycon_ctlr_handle_event(struct joycon_ctlr *ctlr, u8 *data, + int size) +{ + int ret = 0; + bool match = false; + struct joycon_input_report *report; + + if (unlikely(mutex_is_locked(&ctlr->output_mutex)) && + ctlr->msg_type != JOYCON_MSG_TYPE_NONE) { + switch (ctlr->msg_type) { + case JOYCON_MSG_TYPE_USB: + if (size < 2) + break; + if (data[0] == JC_INPUT_USB_RESPONSE && + data[1] == ctlr->usb_ack_match) + match = true; + break; + case JOYCON_MSG_TYPE_SUBCMD: + if (size < sizeof(struct joycon_input_report) || + data[0] != JC_INPUT_SUBCMD_REPLY) + break; + report = (struct joycon_input_report *)data; + if (report->reply.id == ctlr->subcmd_ack_match) + match = true; + break; + default: + break; + } + + if (match) { + memcpy(ctlr->input_buf, data, + min(size, (int)JC_MAX_RESP_SIZE)); + ctlr->msg_type = JOYCON_MSG_TYPE_NONE; + ctlr->received_resp = true; + wake_up(&ctlr->wait); + + /* This message has been handled */ + return 1; + } + } + + if (ctlr->ctlr_state == JOYCON_CTLR_STATE_READ) + ret = joycon_ctlr_read_handler(ctlr, data, size); + + return ret; +} + +static int nintendo_hid_event(struct hid_device *hdev, + struct hid_report *report, u8 *raw_data, int size) +{ + struct joycon_ctlr *ctlr = hid_get_drvdata(hdev); + + if (size < 1) + return -EINVAL; + + return joycon_ctlr_handle_event(ctlr, raw_data, size); +} + +static int nintendo_hid_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + int ret; + struct joycon_ctlr *ctlr; + + hid_dbg(hdev, "probe - start\n"); + + ctlr = devm_kzalloc(&hdev->dev, sizeof(*ctlr), GFP_KERNEL); + if (!ctlr) { + ret = -ENOMEM; + goto err; + } + + ctlr->hdev = hdev; + ctlr->ctlr_state = JOYCON_CTLR_STATE_INIT; + hid_set_drvdata(hdev, ctlr); + mutex_init(&ctlr->output_mutex); + init_waitqueue_head(&ctlr->wait); + + ret = hid_parse(hdev); + if (ret) { + hid_err(hdev, "HID parse failed\n"); + goto err; + } + + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); + if (ret) { + hid_err(hdev, "HW start failed\n"); + goto err; + } + + ret = hid_hw_open(hdev); + if (ret) { + hid_err(hdev, "cannot start hardware I/O\n"); + goto err_stop; + } + + hid_device_io_start(hdev); + + /* Initialize the controller */ + mutex_lock(&ctlr->output_mutex); + /* if handshake command fails, assume ble pro controller */ + if (hdev->product == USB_DEVICE_ID_NINTENDO_PROCON && + !joycon_send_usb(ctlr, JC_USB_CMD_HANDSHAKE)) { + hid_dbg(hdev, "detected USB controller\n"); + /* set baudrate for improved latency */ + ret = joycon_send_usb(ctlr, JC_USB_CMD_BAUDRATE_3M); + if (ret) { + hid_err(hdev, "Failed to set baudrate; ret=%d\n", ret); + goto err_mutex; + } + /* handshake */ + ret = joycon_send_usb(ctlr, JC_USB_CMD_HANDSHAKE); + if (ret) { + hid_err(hdev, "Failed handshake; ret=%d\n", ret); + goto err_mutex; + } + /* + * Set no timeout (to keep controller in USB mode). + * This doesn't send a response, so ignore the timeout. + */ + joycon_send_usb(ctlr, JC_USB_CMD_NO_TIMEOUT); + } + + /* get controller calibration data, and parse it */ + ret = joycon_request_calibration(ctlr); + if (ret) { + /* + * We can function with default calibration, but it may be + * inaccurate. Provide a warning, and continue on. + */ + hid_warn(hdev, "Analog stick positions may be inaccurate\n"); + } + + /* Set the reporting mode to 0x30, which is the full report mode */ + ret = joycon_set_report_mode(ctlr); + if (ret) { + hid_err(hdev, "Failed to set report mode; ret=%d\n", ret); + goto err_mutex; + } + + mutex_unlock(&ctlr->output_mutex); + + ret = joycon_input_create(ctlr); + if (ret) { + hid_err(hdev, "Failed to create input device; ret=%d\n", ret); + goto err_close; + } + + ctlr->ctlr_state = JOYCON_CTLR_STATE_READ; + + hid_dbg(hdev, "probe - success\n"); + return 0; + +err_mutex: + mutex_unlock(&ctlr->output_mutex); +err_close: + hid_hw_close(hdev); +err_stop: + hid_hw_stop(hdev); +err: + hid_err(hdev, "probe - fail = %d\n", ret); + return ret; +} + +static void nintendo_hid_remove(struct hid_device *hdev) +{ + struct joycon_ctlr *ctlr = hid_get_drvdata(hdev); + + hid_dbg(hdev, "remove\n"); + hid_hw_close(hdev); + hid_hw_stop(hdev); +} + +static const struct hid_device_id nintendo_hid_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_NINTENDO, + USB_DEVICE_ID_NINTENDO_PROCON) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, + USB_DEVICE_ID_NINTENDO_PROCON) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, + USB_DEVICE_ID_NINTENDO_JOYCONL) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, + USB_DEVICE_ID_NINTENDO_JOYCONR) }, + { } +}; +MODULE_DEVICE_TABLE(hid, nintendo_hid_devices); + +static struct hid_driver nintendo_hid_driver = { + .name = "nintendo", + .id_table = nintendo_hid_devices, + .probe = nintendo_hid_probe, + .remove = nintendo_hid_remove, + .raw_event = nintendo_hid_event, +}; +module_hid_driver(nintendo_hid_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Daniel J. Ogorchock "); +MODULE_DESCRIPTION("Driver for Nintendo Switch Controllers"); From patchwork Wed Nov 6 06:06:40 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Ogorchock X-Patchwork-Id: 11229341 X-Patchwork-Delegate: jikos@jikos.cz Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id BAA4115AB for ; Wed, 6 Nov 2019 06:06:59 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 839FD2087E for ; Wed, 6 Nov 2019 06:06:59 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="JXtliQZ/" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729937AbfKFGG7 (ORCPT ); Wed, 6 Nov 2019 01:06:59 -0500 Received: from mail-yb1-f194.google.com ([209.85.219.194]:36220 "EHLO mail-yb1-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725812AbfKFGG7 (ORCPT ); Wed, 6 Nov 2019 01:06:59 -0500 Received: by mail-yb1-f194.google.com with SMTP id v17so3510478ybs.3 for ; Tue, 05 Nov 2019 22:06:58 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=jX4DGcyJnk4xI70Z9AvXghk8Adm9wrlKyUJplQyV5E4=; b=JXtliQZ/px1s5eyBao1ftcnCQ4JK6zwOA3TtzsX4nKEIOPpSgcRPt2hM6ejn1qnZp2 FVxqvLbUubo7a7eUPkFkxmI6GoSTMV9IY5jWMVi56Gn3HLSejHeP2yyJPMu+UKqJdP+B R7r3641P42kHLrq9nhARQfAq4DHcSzT0csj4Mh+neqM/EoWA2GCq2qt1zUUgmlNxgXXe UzxNr0mdpM+0nh7EcBrR3GC7+CEcPfCLCpjGX7zA9vdMawCVaJogrT2E0/tIDmIt0SyM sScJY6fqI2QOL026Bqj6XrcChUCAz2321NGsKRMMo8GyTmDzoPiSOeKrZ2Cq+2eU+tnt 1NFg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=jX4DGcyJnk4xI70Z9AvXghk8Adm9wrlKyUJplQyV5E4=; b=JyUxbyfcnorMUODeuiJ4tVCUO1EyuGXFSRRe/aNAjLpYXKmuWRV7nbNPIWd5kzizTN CixaRDp3O4f+T20BOw5AfMiRmGUcwkzS78ph0FiO8Eox47pccbx9stylRPzbOVuvX+Zp eDFJqj9JSgExreWjfEjCM3e3c0ACrPVXmAyYIzfajH6dN8ikJts9LWmVFitd5FAiUyEP C8MGEfUlQLv8f8m1PJn4HemM24yYBQ/CUr2n5Mm3YthstzJ4WeOnESXSQnbFgzJ4W/0S 21eXa1pP6IGrnlv3iIEAEPYWi2VY1cHC1vsR+FXMvI5YDJ17+Zrkw6oEhy+QRhz8i9p2 2kxw== X-Gm-Message-State: APjAAAVBf7GaO0l/vr5/xypTw34XarUZ9Own0Y1895SrETH/8/kFLeu6 CW3BbvpkUHrS0P0R5HAQ6TcNfJXJcqUSZQ== X-Google-Smtp-Source: APXvYqz/KPngKCnHIlDK41r4sOymCM00hrBEhTO02Px566pdc1aWWc3OHWcKFvRgWE+PjIsqNDFrAA== X-Received: by 2002:a25:4202:: with SMTP id p2mr772845yba.422.1573020417492; Tue, 05 Nov 2019 22:06:57 -0800 (PST) Received: from Arrakis.djogorchock.com ([136.33.205.58]) by smtp.gmail.com with ESMTPSA id e82sm11039596ywb.87.2019.11.05.22.06.56 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 05 Nov 2019 22:06:56 -0800 (PST) From: "Daniel J. Ogorchock" To: linux-input@vger.kernel.org Cc: thunderbird2k@gmail.com, blaws05@gmail.com, benjamin.tissoires@redhat.com, jikos@kernel.org, Roderick.Colenbrander@sony.com, svv@google.com, s.jegen@gmail.com, "Daniel J. Ogorchock" Subject: [PATCH v9 02/10] HID: nintendo: add player led support Date: Wed, 6 Nov 2019 00:06:40 -0600 Message-Id: <20191106060648.3018478-3-djogorchock@gmail.com> X-Mailer: git-send-email 2.23.0 In-Reply-To: <20191106060648.3018478-1-djogorchock@gmail.com> References: <20191106060648.3018478-1-djogorchock@gmail.com> MIME-Version: 1.0 Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org This patch adds led_classdev functionality to the switch controller driver. It adds support for the 4 player LEDs. The Home Button LED still needs to be supported on the pro controllers and right joy-con. Signed-off-by: Daniel J. Ogorchock --- drivers/hid/Kconfig | 2 + drivers/hid/hid-nintendo.c | 95 +++++++++++++++++++++++++++++++++++++- 2 files changed, 95 insertions(+), 2 deletions(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 47ec750c2baf..41f7da50896f 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -695,6 +695,8 @@ config HID_MULTITOUCH config HID_NINTENDO tristate "Nintendo Joy-Con and Pro Controller support" depends on HID + depends on NEW_LEDS + depends on LEDS_CLASS help Adds support for the Nintendo Switch Joy-Cons and Pro Controller. All controllers support bluetooth, and the Pro Controller also supports diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c index 882b3758c3be..21d19bc96792 100644 --- a/drivers/hid/hid-nintendo.c +++ b/drivers/hid/hid-nintendo.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -178,11 +179,13 @@ struct joycon_input_report { } __packed; #define JC_MAX_RESP_SIZE (sizeof(struct joycon_input_report) + 35) +#define JC_NUM_LEDS 4 /* Each physical controller is associated with a joycon_ctlr struct */ struct joycon_ctlr { struct hid_device *hdev; struct input_dev *input; + struct led_classdev leds[JC_NUM_LEDS]; enum joycon_ctlr_state ctlr_state; /* The following members are used for synchronous sends/receives */ @@ -517,11 +520,9 @@ static const unsigned int joycon_button_inputs[] = { 0 /* 0 signals end of array */ }; -static DEFINE_MUTEX(joycon_input_num_mutex); static int joycon_input_create(struct joycon_ctlr *ctlr) { struct hid_device *hdev; - static int input_num = 1; const char *name; int ret; int i; @@ -576,6 +577,66 @@ static int joycon_input_create(struct joycon_ctlr *ctlr) if (ret) return ret; + return 0; +} + +static int joycon_player_led_brightness_set(struct led_classdev *led, + enum led_brightness brightness) +{ + struct device *dev = led->dev->parent; + struct hid_device *hdev = to_hid_device(dev); + struct joycon_ctlr *ctlr; + int val = 0; + int i; + int ret; + int num; + + ctlr = hid_get_drvdata(hdev); + if (!ctlr) { + hid_err(hdev, "No controller data\n"); + return -ENODEV; + } + + /* determine which player led this is */ + for (num = 0; num < JC_NUM_LEDS; num++) { + if (&ctlr->leds[num] == led) + break; + } + if (num >= JC_NUM_LEDS) + return -EINVAL; + + mutex_lock(&ctlr->output_mutex); + for (i = 0; i < JC_NUM_LEDS; i++) { + if (i == num) + val |= brightness << i; + else + val |= ctlr->leds[i].brightness << i; + } + ret = joycon_set_player_leds(ctlr, 0, val); + mutex_unlock(&ctlr->output_mutex); + + return ret; +} + +static const char * const joycon_player_led_names[] = { + "player1", + "player2", + "player3", + "player4" +}; + +static DEFINE_MUTEX(joycon_input_num_mutex); +static int joycon_player_leds_create(struct joycon_ctlr *ctlr) +{ + struct hid_device *hdev = ctlr->hdev; + struct device *dev = &hdev->dev; + const char *d_name = dev_name(dev); + struct led_classdev *led; + char *name; + int ret = 0; + int i; + static int input_num = 1; + /* Set the default controller player leds based on controller number */ mutex_lock(&joycon_input_num_mutex); mutex_lock(&ctlr->output_mutex); @@ -583,6 +644,29 @@ static int joycon_input_create(struct joycon_ctlr *ctlr) if (ret) hid_warn(ctlr->hdev, "Failed to set leds; ret=%d\n", ret); mutex_unlock(&ctlr->output_mutex); + + /* configure the player LEDs */ + for (i = 0; i < JC_NUM_LEDS; i++) { + name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s", d_name, + joycon_player_led_names[i]); + if (!name) + return -ENOMEM; + + led = &ctlr->leds[i]; + led->name = name; + led->brightness = ((i + 1) <= input_num) ? LED_ON : LED_OFF; + led->max_brightness = LED_ON; + led->brightness_set_blocking = + joycon_player_led_brightness_set; + led->flags = LED_CORE_SUSPENDRESUME | LED_HW_PLUGGABLE; + + ret = devm_led_classdev_register(&hdev->dev, led); + if (ret) { + hid_err(hdev, "Failed registering %s LED\n", led->name); + break; + } + } + if (++input_num > 4) input_num = 1; mutex_unlock(&joycon_input_num_mutex); @@ -754,6 +838,13 @@ static int nintendo_hid_probe(struct hid_device *hdev, goto err_close; } + /* Initialize the leds */ + ret = joycon_player_leds_create(ctlr); + if (ret) { + hid_err(hdev, "Failed to create leds; ret=%d\n", ret); + goto err_close; + } + ctlr->ctlr_state = JOYCON_CTLR_STATE_READ; hid_dbg(hdev, "probe - success\n"); From patchwork Wed Nov 6 06:06:41 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Ogorchock X-Patchwork-Id: 11229343 X-Patchwork-Delegate: jikos@jikos.cz Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 1B54E1515 for ; Wed, 6 Nov 2019 06:07:01 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id D6CD32087E for ; Wed, 6 Nov 2019 06:07:00 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="bMi4oNfS" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729824AbfKFGHA (ORCPT ); Wed, 6 Nov 2019 01:07:00 -0500 Received: from mail-yb1-f193.google.com ([209.85.219.193]:33545 "EHLO mail-yb1-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725812AbfKFGHA (ORCPT ); Wed, 6 Nov 2019 01:07:00 -0500 Received: by mail-yb1-f193.google.com with SMTP id i15so1533308ybq.0 for ; Tue, 05 Nov 2019 22:06:59 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=qhDZA4XMDpBgvlpLgnnKNyx03azHcmktwpA0EyvKur0=; b=bMi4oNfSGbBRKrwqFiuMGYkUsXQjdjoen/1zrLuEMY3ttkFztk/hyLXezcsj9SaXyE kUecCdOCZoHdtJ2Y+2A6DS9k/m70fue2/gUeTxHpWeT+hncXD9kJwkc2ZkM8toirAaWT +iKPOF3GAm1BZU6E/XYjyyn1QK84wB+ysMytHLt0KL6SnIem1pEyqDuj7i3HOZ+56Ycp HKfRkTaKEums8a7R//u7mZxbj+H0mfuoGiMW0ZreHIakxjPoAdRvnbUFrfg6LnbvFSUc LAdZqu15BWz/ncBdYzPoJx5L1i3nYNCdNLaWm1z9PDM1gYCvX7ET7yeYuK+9hs5neK7g ryAA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=qhDZA4XMDpBgvlpLgnnKNyx03azHcmktwpA0EyvKur0=; b=Z9eCJq6omg7pVSyRZsP4aHhyOjTAgpKGhWyFw8/dAAfzhYRg2GcCfnypfcMF2lLhmK bw8U32UyPJ9A7tojTIDGFFmUhY56blbuOr9dcQPEaw6z4t3TSWfsAD+h+TZ1JPGb/FJc e8Y3L21Kj7sVC4lDdXB31iGsfOQtjPfk5Rvz34X/BW6MTlouRB4/wY0cGU1q688uvXTU t4gMnK1lDzxJjgDIHorIdtY92Xa9XJGwlvl8iyXCUtBGYrTw89K+pb/vooXA49sJ5CX8 VBNNHukaYmTZ2p+bM+2/iLNagO0YJmFOArhDLdSdRl68JFsT2EII+IV90ZNY3lr59dFq k2nA== X-Gm-Message-State: APjAAAXV6LD/FyZyEBnvZDImr3zBpP238HmRp0mmKwQpQWm0I3uPCisj I3O00yzIh4LV819aBs5upNxH5VaQM/BLog== X-Google-Smtp-Source: APXvYqyvc3K9A50GE/yHOhbZxqENT/c0rMm/wWZ6zAXjyWc1m5wiITOncBoJwaB8V/SRZlSO9bOlZw== X-Received: by 2002:a25:790d:: with SMTP id u13mr754918ybc.504.1573020419053; Tue, 05 Nov 2019 22:06:59 -0800 (PST) Received: from Arrakis.djogorchock.com ([136.33.205.58]) by smtp.gmail.com with ESMTPSA id e82sm11039596ywb.87.2019.11.05.22.06.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 05 Nov 2019 22:06:58 -0800 (PST) From: "Daniel J. Ogorchock" To: linux-input@vger.kernel.org Cc: thunderbird2k@gmail.com, blaws05@gmail.com, benjamin.tissoires@redhat.com, jikos@kernel.org, Roderick.Colenbrander@sony.com, svv@google.com, s.jegen@gmail.com, "Daniel J. Ogorchock" Subject: [PATCH v9 03/10] HID: nintendo: add power supply support Date: Wed, 6 Nov 2019 00:06:41 -0600 Message-Id: <20191106060648.3018478-4-djogorchock@gmail.com> X-Mailer: git-send-email 2.23.0 In-Reply-To: <20191106060648.3018478-1-djogorchock@gmail.com> References: <20191106060648.3018478-1-djogorchock@gmail.com> MIME-Version: 1.0 Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org This patch adds power_supply functionality to the switch controller driver for its battery. Signed-off-by: Daniel J. Ogorchock --- drivers/hid/Kconfig | 1 + drivers/hid/hid-nintendo.c | 133 +++++++++++++++++++++++++++++++++++++ 2 files changed, 134 insertions(+) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 41f7da50896f..a6e0f399bcab 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -697,6 +697,7 @@ config HID_NINTENDO depends on HID depends on NEW_LEDS depends on LEDS_CLASS + select POWER_SUPPLY help Adds support for the Nintendo Switch Joy-Cons and Pro Controller. All controllers support bluetooth, and the Pro Controller also supports diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c index 21d19bc96792..0caa9e1860ad 100644 --- a/drivers/hid/hid-nintendo.c +++ b/drivers/hid/hid-nintendo.c @@ -11,6 +11,7 @@ * https://github.com/MTCKC/ProconXInput * hid-wiimote kernel hid driver * hid-logitech-hidpp driver + * hid-sony driver * * This driver supports the Nintendo Switch Joy-Cons and Pro Controllers. The * Pro Controllers can either be used over USB or Bluetooth. @@ -27,6 +28,7 @@ #include #include #include +#include #include /* @@ -187,6 +189,7 @@ struct joycon_ctlr { struct input_dev *input; struct led_classdev leds[JC_NUM_LEDS]; enum joycon_ctlr_state ctlr_state; + spinlock_t lock; /* The following members are used for synchronous sends/receives */ enum joycon_msg_type msg_type; @@ -204,6 +207,12 @@ struct joycon_ctlr { struct joycon_stick_cal right_stick_cal_x; struct joycon_stick_cal right_stick_cal_y; + /* power supply data */ + struct power_supply *battery; + struct power_supply_desc battery_desc; + u8 battery_capacity; + bool battery_charging; + bool host_powered; }; static int __joycon_hid_send(struct hid_device *hdev, u8 *data, size_t len) @@ -434,9 +443,41 @@ static void joycon_parse_report(struct joycon_ctlr *ctlr, struct joycon_input_report *rep) { struct input_dev *dev = ctlr->input; + unsigned long flags; + u8 tmp; u32 btns; u32 id = ctlr->hdev->product; + /* Parse the battery status */ + tmp = rep->bat_con; + spin_lock_irqsave(&ctlr->lock, flags); + ctlr->host_powered = tmp & BIT(0); + ctlr->battery_charging = tmp & BIT(4); + tmp = tmp >> 5; + switch (tmp) { + case 0: /* empty */ + ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_CRITICAL; + break; + case 1: /* low */ + ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_LOW; + break; + case 2: /* medium */ + ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_NORMAL; + break; + case 3: /* high */ + ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_HIGH; + break; + case 4: /* full */ + ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_FULL; + break; + default: + ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN; + hid_warn(ctlr->hdev, "Invalid battery status\n"); + break; + } + spin_unlock_irqrestore(&ctlr->lock, flags); + + /* Parse the buttons and sticks */ btns = hid_field_extract(ctlr->hdev, rep->button_status, 0, 24); if (id != USB_DEVICE_ID_NINTENDO_JOYCONR) { @@ -674,6 +715,90 @@ static int joycon_player_leds_create(struct joycon_ctlr *ctlr) return 0; } +static int joycon_battery_get_property(struct power_supply *supply, + enum power_supply_property prop, + union power_supply_propval *val) +{ + struct joycon_ctlr *ctlr = power_supply_get_drvdata(supply); + unsigned long flags; + int ret = 0; + u8 capacity; + bool charging; + bool powered; + + spin_lock_irqsave(&ctlr->lock, flags); + capacity = ctlr->battery_capacity; + charging = ctlr->battery_charging; + powered = ctlr->host_powered; + spin_unlock_irqrestore(&ctlr->lock, flags); + + switch (prop) { + case POWER_SUPPLY_PROP_PRESENT: + val->intval = 1; + break; + case POWER_SUPPLY_PROP_SCOPE: + val->intval = POWER_SUPPLY_SCOPE_DEVICE; + break; + case POWER_SUPPLY_PROP_CAPACITY_LEVEL: + val->intval = capacity; + break; + case POWER_SUPPLY_PROP_STATUS: + if (charging) + val->intval = POWER_SUPPLY_STATUS_CHARGING; + else if (capacity == POWER_SUPPLY_CAPACITY_LEVEL_FULL && + powered) + val->intval = POWER_SUPPLY_STATUS_FULL; + else + val->intval = POWER_SUPPLY_STATUS_DISCHARGING; + break; + default: + ret = -EINVAL; + break; + } + return ret; +} + +static enum power_supply_property joycon_battery_props[] = { + POWER_SUPPLY_PROP_PRESENT, + POWER_SUPPLY_PROP_CAPACITY_LEVEL, + POWER_SUPPLY_PROP_SCOPE, + POWER_SUPPLY_PROP_STATUS, +}; + +static int joycon_power_supply_create(struct joycon_ctlr *ctlr) +{ + struct hid_device *hdev = ctlr->hdev; + struct power_supply_config supply_config = { .drv_data = ctlr, }; + const char * const name_fmt = "nintendo_switch_controller_battery_%s"; + int ret = 0; + + /* Set initially to unknown before receiving first input report */ + ctlr->battery_capacity = POWER_SUPPLY_CAPACITY_LEVEL_UNKNOWN; + + /* Configure the battery's description */ + ctlr->battery_desc.properties = joycon_battery_props; + ctlr->battery_desc.num_properties = + ARRAY_SIZE(joycon_battery_props); + ctlr->battery_desc.get_property = joycon_battery_get_property; + ctlr->battery_desc.use_for_apm = 0; + ctlr->battery_desc.name = devm_kasprintf(&hdev->dev, GFP_KERNEL, + name_fmt, + dev_name(&hdev->dev)); + if (!ctlr->battery_desc.name) + return -ENOMEM; + + ctlr->battery = devm_power_supply_register(&hdev->dev, + &ctlr->battery_desc, + &supply_config); + if (IS_ERR(ctlr->battery)) { + ret = PTR_ERR(ctlr->battery); + hid_err(hdev, "Failed to register battery; ret=%d\n", ret); + return ret; + } + power_supply_powers(ctlr->battery, &hdev->dev); + return 0; +} + /* Common handler for parsing inputs */ static int joycon_ctlr_read_handler(struct joycon_ctlr *ctlr, u8 *data, int size) @@ -767,6 +892,7 @@ static int nintendo_hid_probe(struct hid_device *hdev, hid_set_drvdata(hdev, ctlr); mutex_init(&ctlr->output_mutex); init_waitqueue_head(&ctlr->wait); + spin_lock_init(&ctlr->lock); ret = hid_parse(hdev); if (ret) { @@ -845,6 +971,13 @@ static int nintendo_hid_probe(struct hid_device *hdev, goto err_close; } + /* Initialize the battery power supply */ + ret = joycon_power_supply_create(ctlr); + if (ret) { + hid_err(hdev, "Failed to create power_supply; ret=%d\n", ret); + goto err_close; + } + ctlr->ctlr_state = JOYCON_CTLR_STATE_READ; hid_dbg(hdev, "probe - success\n"); From patchwork Wed Nov 6 06:06:42 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Ogorchock X-Patchwork-Id: 11229345 X-Patchwork-Delegate: jikos@jikos.cz Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id CBBEA1515 for ; Wed, 6 Nov 2019 06:07:02 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 9E035217D7 for ; Wed, 6 Nov 2019 06:07:02 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="qriRfM27" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729940AbfKFGHC (ORCPT ); Wed, 6 Nov 2019 01:07:02 -0500 Received: from mail-yb1-f195.google.com ([209.85.219.195]:46576 "EHLO mail-yb1-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725812AbfKFGHC (ORCPT ); Wed, 6 Nov 2019 01:07:02 -0500 Received: by mail-yb1-f195.google.com with SMTP id g17so1015646ybd.13 for ; Tue, 05 Nov 2019 22:07:01 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=mAerNiX3pHNJmZUc5Xvu2jJ4CMv6K63g69YkCxZeZvI=; b=qriRfM278IirOVsqkohaPdTEfDH2enPNg+Ia7JDwJI7fMF3R9Vxo0edZI5l5QR1OGx wlCbevgqhhJaipuudG18q6Bwd/mjqHFU1ih1zqEQD/CtNLm4OX9Dt/fyFjAUcyJml5LL zuEr615yr46kDdwONVYsOkf3G/bmfFav57OdQQFglCSVSXW6MRF6c5WyIkGmy26AEcgd 9/87V8F4aUyaQa61No59sIHVLx8CoBBC/Kbq7v0GLj0ZUoiufFwU9g3RqRlKRFXwgcbB B4o3x888ipTcBnbcD873YiVxT4lSsEXCBRSBgxLEjB7oU8AiBXmNweIgCXrh2X8GT7/m 4v3g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=mAerNiX3pHNJmZUc5Xvu2jJ4CMv6K63g69YkCxZeZvI=; b=jxGBfjoFvmazA5k1ErwY0DAjVL6R2rxodMSROQ+1Y6oMIpsC6DEfops+foMLCdKkIE ocN1ZMT02lpBXTGVaO03+1ChyZkIu87zCocKlnQZnzBlpgEskBgv+lPFR/kCQQySBNwD 9lVZiJfJXdVvl8XgKdIzlsC950m9n1Uu/iEXFrxy+MBBcY703ZBGt9HM1nswN4LGm7d7 fMkrsOjXmhxplR0w+TU5jANLsqOilnyhY3gxNGy5cczu+JTUbKCQzMIc3ld26cH3TJl+ 5FLden5/xUXVv4JXYJurPntPVGkeZWkX2k4JHrDEdWBqFavgP5L2osat/5Ae9OoM1Pj8 0buQ== X-Gm-Message-State: APjAAAVeLCzO1vly6ObdBjcad/8eF6lq1Kf8LUHu26x/2QxUpww7Juf5 TjzspxLs1pq45D4SNv5u6q4+pn9RadtgcA== X-Google-Smtp-Source: APXvYqy4ET0AGM3Twc6wsLDEgVhkuob4Pow2k2Tw1I7vOY0ipvrDp0EE8DeQ+5oamoPMNoiqxDn9lg== X-Received: by 2002:a25:b21f:: with SMTP id i31mr810470ybj.9.1573020420934; Tue, 05 Nov 2019 22:07:00 -0800 (PST) Received: from Arrakis.djogorchock.com ([136.33.205.58]) by smtp.gmail.com with ESMTPSA id e82sm11039596ywb.87.2019.11.05.22.06.59 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 05 Nov 2019 22:06:59 -0800 (PST) From: "Daniel J. Ogorchock" To: linux-input@vger.kernel.org Cc: thunderbird2k@gmail.com, blaws05@gmail.com, benjamin.tissoires@redhat.com, jikos@kernel.org, Roderick.Colenbrander@sony.com, svv@google.com, s.jegen@gmail.com, "Daniel J. Ogorchock" Subject: [PATCH v9 04/10] HID: nintendo: add home led support Date: Wed, 6 Nov 2019 00:06:42 -0600 Message-Id: <20191106060648.3018478-5-djogorchock@gmail.com> X-Mailer: git-send-email 2.23.0 In-Reply-To: <20191106060648.3018478-1-djogorchock@gmail.com> References: <20191106060648.3018478-1-djogorchock@gmail.com> MIME-Version: 1.0 Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org This patch adds the ability to set the intensity level of the home button's LED. Signed-off-by: Daniel J. Ogorchock --- drivers/hid/hid-nintendo.c | 69 +++++++++++++++++++++++++++++++++++--- 1 file changed, 65 insertions(+), 4 deletions(-) diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c index 0caa9e1860ad..18be44214041 100644 --- a/drivers/hid/hid-nintendo.c +++ b/drivers/hid/hid-nintendo.c @@ -187,7 +187,8 @@ struct joycon_input_report { struct joycon_ctlr { struct hid_device *hdev; struct input_dev *input; - struct led_classdev leds[JC_NUM_LEDS]; + struct led_classdev leds[JC_NUM_LEDS]; /* player leds */ + struct led_classdev home_led; enum joycon_ctlr_state ctlr_state; spinlock_t lock; @@ -659,6 +660,40 @@ static int joycon_player_led_brightness_set(struct led_classdev *led, return ret; } +static int joycon_home_led_brightness_set(struct led_classdev *led, + enum led_brightness brightness) +{ + struct device *dev = led->dev->parent; + struct hid_device *hdev = to_hid_device(dev); + struct joycon_ctlr *ctlr; + struct joycon_subcmd_request *req; + u8 buffer[sizeof(*req) + 5] = { 0 }; + u8 *data; + int ret; + + ctlr = hid_get_drvdata(hdev); + if (!ctlr) { + hid_err(hdev, "No controller data\n"); + return -ENODEV; + } + + req = (struct joycon_subcmd_request *)buffer; + req->subcmd_id = JC_SUBCMD_SET_HOME_LIGHT; + data = req->data; + data[0] = 0x01; + data[1] = brightness << 4; + data[2] = brightness | (brightness << 4); + data[3] = 0x11; + data[4] = 0x11; + + hid_dbg(hdev, "setting home led brightness\n"); + mutex_lock(&ctlr->output_mutex); + ret = joycon_send_subcmd(ctlr, req, 5); + mutex_unlock(&ctlr->output_mutex); + + return ret; +} + static const char * const joycon_player_led_names[] = { "player1", "player2", @@ -667,7 +702,7 @@ static const char * const joycon_player_led_names[] = { }; static DEFINE_MUTEX(joycon_input_num_mutex); -static int joycon_player_leds_create(struct joycon_ctlr *ctlr) +static int joycon_leds_create(struct joycon_ctlr *ctlr) { struct hid_device *hdev = ctlr->hdev; struct device *dev = &hdev->dev; @@ -704,7 +739,7 @@ static int joycon_player_leds_create(struct joycon_ctlr *ctlr) ret = devm_led_classdev_register(&hdev->dev, led); if (ret) { hid_err(hdev, "Failed registering %s LED\n", led->name); - break; + return ret; } } @@ -712,6 +747,32 @@ static int joycon_player_leds_create(struct joycon_ctlr *ctlr) input_num = 1; mutex_unlock(&joycon_input_num_mutex); + /* configure the home LED */ + if (ctlr->hdev->product != USB_DEVICE_ID_NINTENDO_JOYCONL) { + name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s", d_name, "home"); + if (!name) + return ret; + + led = &ctlr->home_led; + led->name = name; + led->brightness = 0; + led->max_brightness = 0xF; + led->brightness_set_blocking = joycon_home_led_brightness_set; + led->flags = LED_CORE_SUSPENDRESUME | LED_HW_PLUGGABLE; + ret = devm_led_classdev_register(&hdev->dev, led); + if (ret) { + hid_err(hdev, "Failed registering home led\n"); + return ret; + } + /* Set the home LED to 0 as default state */ + ret = joycon_home_led_brightness_set(led, 0); + if (ret) { + hid_err(hdev, "Failed to set home LED dflt; ret=%d\n", + ret); + return ret; + } + } + return 0; } @@ -965,7 +1026,7 @@ static int nintendo_hid_probe(struct hid_device *hdev, } /* Initialize the leds */ - ret = joycon_player_leds_create(ctlr); + ret = joycon_leds_create(ctlr); if (ret) { hid_err(hdev, "Failed to create leds; ret=%d\n", ret); goto err_close; From patchwork Wed Nov 6 06:06:43 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Ogorchock X-Patchwork-Id: 11229349 X-Patchwork-Delegate: jikos@jikos.cz Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id EF48915AB for ; Wed, 6 Nov 2019 06:07:06 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id AEB79217D7 for ; Wed, 6 Nov 2019 06:07:06 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="ibltB0cp" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730188AbfKFGHG (ORCPT ); Wed, 6 Nov 2019 01:07:06 -0500 Received: from mail-yw1-f67.google.com ([209.85.161.67]:34498 "EHLO mail-yw1-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725812AbfKFGHG (ORCPT ); Wed, 6 Nov 2019 01:07:06 -0500 Received: by mail-yw1-f67.google.com with SMTP id y18so2535335ywk.1 for ; Tue, 05 Nov 2019 22:07:04 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=LMaL0BBjGNgLlzO2w4RJZk8VQS1e+h+MIz8nk8PT72o=; b=ibltB0cpnqamDnyYjSkupCTRA4LGIt8+rSp37G4JLYA29opA6UOx16cPw7wEhGTx+9 0mL7XCDFqWlsvAxYoumM9esHGFvt1nkEGZ0FZvuK4tJeKMOeDA3KDlOqZ38yc3q/A7h5 s7Li3R5zrxbyFIczi3H+JiEJci4U+dolUQEbKJ7d+AOB1gngizBxEis2Vd6NsL9LfUth tfQgKP7oswJwHaC4XLHPL++dL5ifqgd6EhXUppPtnZqOCbdI4NXk134pN6fg8WzXEun9 sJmtkBlEZbj15gFum00yGUTxP6jMeDzjPb2gkk1C2h5kBrl8mnXZbMYtk426osrcCPAX fRGQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=LMaL0BBjGNgLlzO2w4RJZk8VQS1e+h+MIz8nk8PT72o=; b=jHrUv/Z8P+9L4Fsw5neaLMcnwi2WtAlBkx1D8967Xws5q6V5eNhGpTua7Qp4NRDawN lTOFm92KInXQnCdZz4mLLetlqEHFXFNz7pTvtLvpZF0LBPbOhRQ+fxSnwjKlaBjKyDke AXLWXbQkjG9g5YBWZ2ZFclmTge6sM/On+nIzB7YBFbiJf8XQmHCvtLe8FOZUsPpvlUsS mjZTZAmBYfOjzvMMy4a5rYHYJuIzYkcLLfu8o73hQnmoYqbfKwGreDh/DAuWv5wiIvcx sLyiP/V6FdT06IuUu0RZy/TpQbaW8Ng8ymH1sLRq5mRB9pvV/sIZHksKnc1zP7c+65J6 Znzw== X-Gm-Message-State: APjAAAWJE1mObhToGVeI+YQrdQ9TJvt7+Ra4qQNR0fTfhf7NgP6HfzPp lg+rQjYBTQu0H4HkKghBijodu4Wg2vwx0w== X-Google-Smtp-Source: APXvYqyAOiBroGidSwl7930PaS782tdF955EIAI/Vf3Sr7p3xMbRZoHJM1VsWp2Jk5vMOldT6LEKXg== X-Received: by 2002:a81:78d7:: with SMTP id t206mr425820ywc.177.1573020422645; Tue, 05 Nov 2019 22:07:02 -0800 (PST) Received: from Arrakis.djogorchock.com ([136.33.205.58]) by smtp.gmail.com with ESMTPSA id e82sm11039596ywb.87.2019.11.05.22.07.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 05 Nov 2019 22:07:01 -0800 (PST) From: "Daniel J. Ogorchock" To: linux-input@vger.kernel.org Cc: thunderbird2k@gmail.com, blaws05@gmail.com, benjamin.tissoires@redhat.com, jikos@kernel.org, Roderick.Colenbrander@sony.com, svv@google.com, s.jegen@gmail.com, "Daniel J. Ogorchock" Subject: [PATCH v9 05/10] HID: nintendo: add rumble support Date: Wed, 6 Nov 2019 00:06:43 -0600 Message-Id: <20191106060648.3018478-6-djogorchock@gmail.com> X-Mailer: git-send-email 2.23.0 In-Reply-To: <20191106060648.3018478-1-djogorchock@gmail.com> References: <20191106060648.3018478-1-djogorchock@gmail.com> MIME-Version: 1.0 Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org This patch adds support for controller rumble. The ff_effect weak magnitude is associated with the pro controller's right motor (or with a right joy-con). The strong magnitude is associated with the pro's left motor (or a left joy-con). The rumble data is sent periodically (currently configured for every 50 milliseconds). If the controller receives no rumble data for too long a time period, it will stop vibrating. The data is also sent every time joycon_set_rumble is called to avoid latency of up to 50ms. Because the rumble subcommands are sent in a deferred workqueue (they can't be sent in the play_effect function due to the hid send sleeping), the effects are queued. This ensures that no rumble effect is missed due to them arriving in too quick of succession. Signed-off-by: Daniel J. Ogorchock --- drivers/hid/Kconfig | 10 ++ drivers/hid/hid-nintendo.c | 347 ++++++++++++++++++++++++++++++++++++- 2 files changed, 354 insertions(+), 3 deletions(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index a6e0f399bcab..fea15e4e085f 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -706,6 +706,16 @@ config HID_NINTENDO To compile this driver as a module, choose M here: the module will be called hid-nintendo. +config NINTENDO_FF + bool "Nintendo Switch controller force feedback support" + depends on HID_NINTENDO + select INPUT_FF_MEMLESS + help + Say Y here if you have a Nintendo Switch controller and want to enable + force feedback support for it. This works for both joy-cons and the pro + controller. For the pro controller, both rumble motors can be controlled + individually. + config HID_NTI tristate "NTI keyboard adapters" ---help--- diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c index 18be44214041..ec632a4b9785 100644 --- a/drivers/hid/hid-nintendo.c +++ b/drivers/hid/hid-nintendo.c @@ -9,6 +9,7 @@ * https://gitlab.com/pjranki/joycon-linux-kernel (Peter Rankin) * https://github.com/FrotBot/SwitchProConLinuxUSB * https://github.com/MTCKC/ProconXInput + * https://github.com/Davidobot/BetterJoyForCemu * hid-wiimote kernel hid driver * hid-logitech-hidpp driver * hid-sony driver @@ -26,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -105,6 +107,120 @@ static const u16 JC_MAX_STICK_MAG = 32767; static const u16 JC_STICK_FUZZ = 250; static const u16 JC_STICK_FLAT = 500; +/* frequency/amplitude tables for rumble */ +struct joycon_rumble_freq_data { + u16 high; + u8 low; + u16 freq; /* Hz*/ +}; + +struct joycon_rumble_amp_data { + u8 high; + u16 low; + u16 amp; +}; + +/* + * These tables are from + * https://github.com/dekuNukem/Nintendo_Switch_Reverse_Engineering/blob/master/rumble_data_table.md + */ +static const struct joycon_rumble_freq_data joycon_rumble_frequencies[] = { + /* high, low, freq */ + { 0x0000, 0x01, 41 }, { 0x0000, 0x02, 42 }, { 0x0000, 0x03, 43 }, + { 0x0000, 0x04, 44 }, { 0x0000, 0x05, 45 }, { 0x0000, 0x06, 46 }, + { 0x0000, 0x07, 47 }, { 0x0000, 0x08, 48 }, { 0x0000, 0x09, 49 }, + { 0x0000, 0x0A, 50 }, { 0x0000, 0x0B, 51 }, { 0x0000, 0x0C, 52 }, + { 0x0000, 0x0D, 53 }, { 0x0000, 0x0E, 54 }, { 0x0000, 0x0F, 55 }, + { 0x0000, 0x10, 57 }, { 0x0000, 0x11, 58 }, { 0x0000, 0x12, 59 }, + { 0x0000, 0x13, 60 }, { 0x0000, 0x14, 62 }, { 0x0000, 0x15, 63 }, + { 0x0000, 0x16, 64 }, { 0x0000, 0x17, 66 }, { 0x0000, 0x18, 67 }, + { 0x0000, 0x19, 69 }, { 0x0000, 0x1A, 70 }, { 0x0000, 0x1B, 72 }, + { 0x0000, 0x1C, 73 }, { 0x0000, 0x1D, 75 }, { 0x0000, 0x1e, 77 }, + { 0x0000, 0x1f, 78 }, { 0x0000, 0x20, 80 }, { 0x0400, 0x21, 82 }, + { 0x0800, 0x22, 84 }, { 0x0c00, 0x23, 85 }, { 0x1000, 0x24, 87 }, + { 0x1400, 0x25, 89 }, { 0x1800, 0x26, 91 }, { 0x1c00, 0x27, 93 }, + { 0x2000, 0x28, 95 }, { 0x2400, 0x29, 97 }, { 0x2800, 0x2a, 99 }, + { 0x2c00, 0x2b, 102 }, { 0x3000, 0x2c, 104 }, { 0x3400, 0x2d, 106 }, + { 0x3800, 0x2e, 108 }, { 0x3c00, 0x2f, 111 }, { 0x4000, 0x30, 113 }, + { 0x4400, 0x31, 116 }, { 0x4800, 0x32, 118 }, { 0x4c00, 0x33, 121 }, + { 0x5000, 0x34, 123 }, { 0x5400, 0x35, 126 }, { 0x5800, 0x36, 129 }, + { 0x5c00, 0x37, 132 }, { 0x6000, 0x38, 135 }, { 0x6400, 0x39, 137 }, + { 0x6800, 0x3a, 141 }, { 0x6c00, 0x3b, 144 }, { 0x7000, 0x3c, 147 }, + { 0x7400, 0x3d, 150 }, { 0x7800, 0x3e, 153 }, { 0x7c00, 0x3f, 157 }, + { 0x8000, 0x40, 160 }, { 0x8400, 0x41, 164 }, { 0x8800, 0x42, 167 }, + { 0x8c00, 0x43, 171 }, { 0x9000, 0x44, 174 }, { 0x9400, 0x45, 178 }, + { 0x9800, 0x46, 182 }, { 0x9c00, 0x47, 186 }, { 0xa000, 0x48, 190 }, + { 0xa400, 0x49, 194 }, { 0xa800, 0x4a, 199 }, { 0xac00, 0x4b, 203 }, + { 0xb000, 0x4c, 207 }, { 0xb400, 0x4d, 212 }, { 0xb800, 0x4e, 217 }, + { 0xbc00, 0x4f, 221 }, { 0xc000, 0x50, 226 }, { 0xc400, 0x51, 231 }, + { 0xc800, 0x52, 236 }, { 0xcc00, 0x53, 241 }, { 0xd000, 0x54, 247 }, + { 0xd400, 0x55, 252 }, { 0xd800, 0x56, 258 }, { 0xdc00, 0x57, 263 }, + { 0xe000, 0x58, 269 }, { 0xe400, 0x59, 275 }, { 0xe800, 0x5a, 281 }, + { 0xec00, 0x5b, 287 }, { 0xf000, 0x5c, 293 }, { 0xf400, 0x5d, 300 }, + { 0xf800, 0x5e, 306 }, { 0xfc00, 0x5f, 313 }, { 0x0001, 0x60, 320 }, + { 0x0401, 0x61, 327 }, { 0x0801, 0x62, 334 }, { 0x0c01, 0x63, 341 }, + { 0x1001, 0x64, 349 }, { 0x1401, 0x65, 357 }, { 0x1801, 0x66, 364 }, + { 0x1c01, 0x67, 372 }, { 0x2001, 0x68, 381 }, { 0x2401, 0x69, 389 }, + { 0x2801, 0x6a, 397 }, { 0x2c01, 0x6b, 406 }, { 0x3001, 0x6c, 415 }, + { 0x3401, 0x6d, 424 }, { 0x3801, 0x6e, 433 }, { 0x3c01, 0x6f, 443 }, + { 0x4001, 0x70, 453 }, { 0x4401, 0x71, 462 }, { 0x4801, 0x72, 473 }, + { 0x4c01, 0x73, 483 }, { 0x5001, 0x74, 494 }, { 0x5401, 0x75, 504 }, + { 0x5801, 0x76, 515 }, { 0x5c01, 0x77, 527 }, { 0x6001, 0x78, 538 }, + { 0x6401, 0x79, 550 }, { 0x6801, 0x7a, 562 }, { 0x6c01, 0x7b, 574 }, + { 0x7001, 0x7c, 587 }, { 0x7401, 0x7d, 600 }, { 0x7801, 0x7e, 613 }, + { 0x7c01, 0x7f, 626 }, { 0x8001, 0x00, 640 }, { 0x8401, 0x00, 654 }, + { 0x8801, 0x00, 668 }, { 0x8c01, 0x00, 683 }, { 0x9001, 0x00, 698 }, + { 0x9401, 0x00, 713 }, { 0x9801, 0x00, 729 }, { 0x9c01, 0x00, 745 }, + { 0xa001, 0x00, 761 }, { 0xa401, 0x00, 778 }, { 0xa801, 0x00, 795 }, + { 0xac01, 0x00, 812 }, { 0xb001, 0x00, 830 }, { 0xb401, 0x00, 848 }, + { 0xb801, 0x00, 867 }, { 0xbc01, 0x00, 886 }, { 0xc001, 0x00, 905 }, + { 0xc401, 0x00, 925 }, { 0xc801, 0x00, 945 }, { 0xcc01, 0x00, 966 }, + { 0xd001, 0x00, 987 }, { 0xd401, 0x00, 1009 }, { 0xd801, 0x00, 1031 }, + { 0xdc01, 0x00, 1053 }, { 0xe001, 0x00, 1076 }, { 0xe401, 0x00, 1100 }, + { 0xe801, 0x00, 1124 }, { 0xec01, 0x00, 1149 }, { 0xf001, 0x00, 1174 }, + { 0xf401, 0x00, 1199 }, { 0xf801, 0x00, 1226 }, { 0xfc01, 0x00, 1253 } +}; + +#define joycon_max_rumble_amp (1003) +static const struct joycon_rumble_amp_data joycon_rumble_amplitudes[] = { + /* high, low, amp */ + { 0x00, 0x0040, 0 }, + { 0x02, 0x8040, 10 }, { 0x04, 0x0041, 12 }, { 0x06, 0x8041, 14 }, + { 0x08, 0x0042, 17 }, { 0x0a, 0x8042, 20 }, { 0x0c, 0x0043, 24 }, + { 0x0e, 0x8043, 28 }, { 0x10, 0x0044, 33 }, { 0x12, 0x8044, 40 }, + { 0x14, 0x0045, 47 }, { 0x16, 0x8045, 56 }, { 0x18, 0x0046, 67 }, + { 0x1a, 0x8046, 80 }, { 0x1c, 0x0047, 95 }, { 0x1e, 0x8047, 112 }, + { 0x20, 0x0048, 117 }, { 0x22, 0x8048, 123 }, { 0x24, 0x0049, 128 }, + { 0x26, 0x8049, 134 }, { 0x28, 0x004a, 140 }, { 0x2a, 0x804a, 146 }, + { 0x2c, 0x004b, 152 }, { 0x2e, 0x804b, 159 }, { 0x30, 0x004c, 166 }, + { 0x32, 0x804c, 173 }, { 0x34, 0x004d, 181 }, { 0x36, 0x804d, 189 }, + { 0x38, 0x004e, 198 }, { 0x3a, 0x804e, 206 }, { 0x3c, 0x004f, 215 }, + { 0x3e, 0x804f, 225 }, { 0x40, 0x0050, 230 }, { 0x42, 0x8050, 235 }, + { 0x44, 0x0051, 240 }, { 0x46, 0x8051, 245 }, { 0x48, 0x0052, 251 }, + { 0x4a, 0x8052, 256 }, { 0x4c, 0x0053, 262 }, { 0x4e, 0x8053, 268 }, + { 0x50, 0x0054, 273 }, { 0x52, 0x8054, 279 }, { 0x54, 0x0055, 286 }, + { 0x56, 0x8055, 292 }, { 0x58, 0x0056, 298 }, { 0x5a, 0x8056, 305 }, + { 0x5c, 0x0057, 311 }, { 0x5e, 0x8057, 318 }, { 0x60, 0x0058, 325 }, + { 0x62, 0x8058, 332 }, { 0x64, 0x0059, 340 }, { 0x66, 0x8059, 347 }, + { 0x68, 0x005a, 355 }, { 0x6a, 0x805a, 362 }, { 0x6c, 0x005b, 370 }, + { 0x6e, 0x805b, 378 }, { 0x70, 0x005c, 387 }, { 0x72, 0x805c, 395 }, + { 0x74, 0x005d, 404 }, { 0x76, 0x805d, 413 }, { 0x78, 0x005e, 422 }, + { 0x7a, 0x805e, 431 }, { 0x7c, 0x005f, 440 }, { 0x7e, 0x805f, 450 }, + { 0x80, 0x0060, 460 }, { 0x82, 0x8060, 470 }, { 0x84, 0x0061, 480 }, + { 0x86, 0x8061, 491 }, { 0x88, 0x0062, 501 }, { 0x8a, 0x8062, 512 }, + { 0x8c, 0x0063, 524 }, { 0x8e, 0x8063, 535 }, { 0x90, 0x0064, 547 }, + { 0x92, 0x8064, 559 }, { 0x94, 0x0065, 571 }, { 0x96, 0x8065, 584 }, + { 0x98, 0x0066, 596 }, { 0x9a, 0x8066, 609 }, { 0x9c, 0x0067, 623 }, + { 0x9e, 0x8067, 636 }, { 0xa0, 0x0068, 650 }, { 0xa2, 0x8068, 665 }, + { 0xa4, 0x0069, 679 }, { 0xa6, 0x8069, 694 }, { 0xa8, 0x006a, 709 }, + { 0xaa, 0x806a, 725 }, { 0xac, 0x006b, 741 }, { 0xae, 0x806b, 757 }, + { 0xb0, 0x006c, 773 }, { 0xb2, 0x806c, 790 }, { 0xb4, 0x006d, 808 }, + { 0xb6, 0x806d, 825 }, { 0xb8, 0x006e, 843 }, { 0xba, 0x806e, 862 }, + { 0xbc, 0x006f, 881 }, { 0xbe, 0x806f, 900 }, { 0xc0, 0x0070, 920 }, + { 0xc2, 0x8070, 940 }, { 0xc4, 0x0071, 960 }, { 0xc6, 0x8071, 981 }, + { 0xc8, 0x0072, joycon_max_rumble_amp } +}; + /* States for controller state machine */ enum joycon_ctlr_state { JOYCON_CTLR_STATE_INIT, @@ -182,6 +298,12 @@ struct joycon_input_report { #define JC_MAX_RESP_SIZE (sizeof(struct joycon_input_report) + 35) #define JC_NUM_LEDS 4 +#define JC_RUMBLE_DATA_SIZE 8 +#define JC_RUMBLE_QUEUE_SIZE 8 + +static const u16 JC_RUMBLE_DFLT_LOW_FREQ = 160; +static const u16 JC_RUMBLE_DFLT_HIGH_FREQ = 320; +static const u16 JC_RUMBLE_PERIOD_MS = 50; /* Each physical controller is associated with a joycon_ctlr struct */ struct joycon_ctlr { @@ -214,6 +336,18 @@ struct joycon_ctlr { u8 battery_capacity; bool battery_charging; bool host_powered; + + /* rumble */ + u8 rumble_data[JC_RUMBLE_QUEUE_SIZE][JC_RUMBLE_DATA_SIZE]; + int rumble_queue_head; + int rumble_queue_tail; + struct workqueue_struct *rumble_queue; + struct work_struct rumble_worker; + unsigned int rumble_msecs; + u16 rumble_ll_freq; + u16 rumble_lh_freq; + u16 rumble_rl_freq; + u16 rumble_rh_freq; }; static int __joycon_hid_send(struct hid_device *hdev, u8 *data, size_t len) @@ -270,6 +404,12 @@ static int joycon_send_subcmd(struct joycon_ctlr *ctlr, size_t data_len) { int ret; + unsigned long flags; + + spin_lock_irqsave(&ctlr->lock, flags); + memcpy(subcmd->rumble_data, ctlr->rumble_data[ctlr->rumble_queue_tail], + JC_RUMBLE_DATA_SIZE); + spin_unlock_irqrestore(&ctlr->lock, flags); subcmd->output_id = JC_OUTPUT_RUMBLE_AND_SUBCMD; subcmd->packet_num = ctlr->subcmd_num; @@ -422,6 +562,19 @@ static int joycon_set_report_mode(struct joycon_ctlr *ctlr) return joycon_send_subcmd(ctlr, req, 1); } +static int joycon_enable_rumble(struct joycon_ctlr *ctlr, bool enable) +{ + struct joycon_subcmd_request *req; + u8 buffer[sizeof(*req) + 1] = { 0 }; + + req = (struct joycon_subcmd_request *)buffer; + req->subcmd_id = JC_SUBCMD_ENABLE_VIBRATION; + req->data[0] = enable ? 0x01 : 0x00; + + hid_dbg(ctlr->hdev, "%s rumble\n", enable ? "enabling" : "disabling"); + return joycon_send_subcmd(ctlr, req, 1); +} + static s32 joycon_map_stick_val(struct joycon_stick_cal *cal, s32 val) { s32 center = cal->center; @@ -448,10 +601,15 @@ static void joycon_parse_report(struct joycon_ctlr *ctlr, u8 tmp; u32 btns; u32 id = ctlr->hdev->product; + unsigned long msecs = jiffies_to_msecs(jiffies); + + spin_lock_irqsave(&ctlr->lock, flags); + if (IS_ENABLED(CONFIG_NINTENDO_FF) && rep->vibrator_report && + (msecs - ctlr->rumble_msecs) >= JC_RUMBLE_PERIOD_MS) + queue_work(ctlr->rumble_queue, &ctlr->rumble_worker); /* Parse the battery status */ tmp = rep->bat_con; - spin_lock_irqsave(&ctlr->lock, flags); ctlr->host_powered = tmp & BIT(0); ctlr->battery_charging = tmp & BIT(4); tmp = tmp >> 5; @@ -552,6 +710,161 @@ static void joycon_parse_report(struct joycon_ctlr *ctlr, input_sync(dev); } +static void joycon_rumble_worker(struct work_struct *work) +{ + struct joycon_ctlr *ctlr = container_of(work, struct joycon_ctlr, + rumble_worker); + unsigned long flags; + bool again = true; + int ret; + + while (again) { + mutex_lock(&ctlr->output_mutex); + ret = joycon_enable_rumble(ctlr, true); + mutex_unlock(&ctlr->output_mutex); + if (ret < 0) + hid_warn(ctlr->hdev, "Failed to set rumble; e=%d", ret); + + spin_lock_irqsave(&ctlr->lock, flags); + ctlr->rumble_msecs = jiffies_to_msecs(jiffies); + if (ctlr->rumble_queue_tail != ctlr->rumble_queue_head) { + if (++ctlr->rumble_queue_tail >= JC_RUMBLE_QUEUE_SIZE) + ctlr->rumble_queue_tail = 0; + } else { + again = false; + } + spin_unlock_irqrestore(&ctlr->lock, flags); + } +} + +#if IS_ENABLED(CONFIG_NINTENDO_FF) +static struct joycon_rumble_freq_data joycon_find_rumble_freq(u16 freq) +{ + const size_t length = ARRAY_SIZE(joycon_rumble_frequencies); + const struct joycon_rumble_freq_data *data = joycon_rumble_frequencies; + int i = 0; + + if (freq > data[0].freq) { + for (i = 1; i < length - 1; i++) { + if (freq > data[i - 1].freq && freq <= data[i].freq) + break; + } + } + + return data[i]; +} + +static struct joycon_rumble_amp_data joycon_find_rumble_amp(u16 amp) +{ + const size_t length = ARRAY_SIZE(joycon_rumble_amplitudes); + const struct joycon_rumble_amp_data *data = joycon_rumble_amplitudes; + int i = 0; + + if (amp > data[0].amp) { + for (i = 1; i < length - 1; i++) { + if (amp > data[i - 1].amp && amp <= data[i].amp) + break; + } + } + + return data[i]; +} + +static void joycon_encode_rumble(u8 *data, u16 freq_low, u16 freq_high, u16 amp) +{ + struct joycon_rumble_freq_data freq_data_low; + struct joycon_rumble_freq_data freq_data_high; + struct joycon_rumble_amp_data amp_data; + + freq_data_low = joycon_find_rumble_freq(freq_low); + freq_data_high = joycon_find_rumble_freq(freq_high); + amp_data = joycon_find_rumble_amp(amp); + + data[0] = (freq_data_high.high >> 8) & 0xFF; + data[1] = (freq_data_high.high & 0xFF) + amp_data.high; + data[2] = freq_data_low.low + ((amp_data.low >> 8) & 0xFF); + data[3] = amp_data.low & 0xFF; +} + +static const u16 JOYCON_MAX_RUMBLE_HIGH_FREQ = 1253; +static const u16 JOYCON_MIN_RUMBLE_HIGH_FREQ = 82; +static const u16 JOYCON_MAX_RUMBLE_LOW_FREQ = 626; +static const u16 JOYCON_MIN_RUMBLE_LOW_FREQ = 41; + +static void joycon_clamp_rumble_freqs(struct joycon_ctlr *ctlr) +{ + unsigned long flags; + + spin_lock_irqsave(&ctlr->lock, flags); + ctlr->rumble_ll_freq = clamp(ctlr->rumble_ll_freq, + JOYCON_MIN_RUMBLE_LOW_FREQ, + JOYCON_MAX_RUMBLE_LOW_FREQ); + ctlr->rumble_lh_freq = clamp(ctlr->rumble_lh_freq, + JOYCON_MIN_RUMBLE_HIGH_FREQ, + JOYCON_MAX_RUMBLE_HIGH_FREQ); + ctlr->rumble_rl_freq = clamp(ctlr->rumble_rl_freq, + JOYCON_MIN_RUMBLE_LOW_FREQ, + JOYCON_MAX_RUMBLE_LOW_FREQ); + ctlr->rumble_rh_freq = clamp(ctlr->rumble_rh_freq, + JOYCON_MIN_RUMBLE_HIGH_FREQ, + JOYCON_MAX_RUMBLE_HIGH_FREQ); + spin_unlock_irqrestore(&ctlr->lock, flags); +} + +static int joycon_set_rumble(struct joycon_ctlr *ctlr, u16 amp_r, u16 amp_l, + bool schedule_now) +{ + u8 data[JC_RUMBLE_DATA_SIZE]; + u16 amp; + u16 freq_r_low; + u16 freq_r_high; + u16 freq_l_low; + u16 freq_l_high; + unsigned long flags; + + spin_lock_irqsave(&ctlr->lock, flags); + freq_r_low = ctlr->rumble_rl_freq; + freq_r_high = ctlr->rumble_rh_freq; + freq_l_low = ctlr->rumble_ll_freq; + freq_l_high = ctlr->rumble_lh_freq; + spin_unlock_irqrestore(&ctlr->lock, flags); + + /* right joy-con */ + amp = amp_r * (u32)joycon_max_rumble_amp / 65535; + joycon_encode_rumble(data + 4, freq_r_low, freq_r_high, amp); + + /* left joy-con */ + amp = amp_l * (u32)joycon_max_rumble_amp / 65535; + joycon_encode_rumble(data, freq_l_low, freq_l_high, amp); + + spin_lock_irqsave(&ctlr->lock, flags); + if (++ctlr->rumble_queue_head >= JC_RUMBLE_QUEUE_SIZE) + ctlr->rumble_queue_head = 0; + memcpy(ctlr->rumble_data[ctlr->rumble_queue_head], data, + JC_RUMBLE_DATA_SIZE); + spin_unlock_irqrestore(&ctlr->lock, flags); + + /* don't wait for the periodic send (reduces latency) */ + if (schedule_now) + queue_work(ctlr->rumble_queue, &ctlr->rumble_worker); + + return 0; +} + +static int joycon_play_effect(struct input_dev *dev, void *data, + struct ff_effect *effect) +{ + struct joycon_ctlr *ctlr = input_get_drvdata(dev); + + if (effect->type != FF_RUMBLE) + return 0; + + return joycon_set_rumble(ctlr, + effect->u.rumble.weak_magnitude, + effect->u.rumble.strong_magnitude, + true); +} +#endif /* IS_ENABLED(CONFIG_NINTENDO_FF) */ static const unsigned int joycon_button_inputs[] = { BTN_SELECT, BTN_Z, BTN_THUMBL, @@ -615,6 +928,19 @@ static int joycon_input_create(struct joycon_ctlr *ctlr) input_set_capability(ctlr->input, EV_KEY, joycon_button_inputs[i]); +#if IS_ENABLED(CONFIG_NINTENDO_FF) + /* set up rumble */ + input_set_capability(ctlr->input, EV_FF, FF_RUMBLE); + input_ff_create_memless(ctlr->input, NULL, joycon_play_effect); + ctlr->rumble_ll_freq = JC_RUMBLE_DFLT_LOW_FREQ; + ctlr->rumble_lh_freq = JC_RUMBLE_DFLT_HIGH_FREQ; + ctlr->rumble_rl_freq = JC_RUMBLE_DFLT_LOW_FREQ; + ctlr->rumble_rh_freq = JC_RUMBLE_DFLT_HIGH_FREQ; + joycon_clamp_rumble_freqs(ctlr); + joycon_set_rumble(ctlr, 0, 0, false); + ctlr->rumble_msecs = jiffies_to_msecs(jiffies); +#endif + ret = input_register_device(ctlr->input); if (ret) return ret; @@ -950,21 +1276,26 @@ static int nintendo_hid_probe(struct hid_device *hdev, ctlr->hdev = hdev; ctlr->ctlr_state = JOYCON_CTLR_STATE_INIT; + ctlr->rumble_queue_head = JC_RUMBLE_QUEUE_SIZE - 1; + ctlr->rumble_queue_tail = 0; hid_set_drvdata(hdev, ctlr); mutex_init(&ctlr->output_mutex); init_waitqueue_head(&ctlr->wait); spin_lock_init(&ctlr->lock); + ctlr->rumble_queue = alloc_workqueue("hid-nintendo-rumble_wq", + WQ_FREEZABLE | WQ_MEM_RECLAIM, 0); + INIT_WORK(&ctlr->rumble_worker, joycon_rumble_worker); ret = hid_parse(hdev); if (ret) { hid_err(hdev, "HID parse failed\n"); - goto err; + goto err_wq; } ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); if (ret) { hid_err(hdev, "HW start failed\n"); - goto err; + goto err_wq; } ret = hid_hw_open(hdev); @@ -1017,6 +1348,13 @@ static int nintendo_hid_probe(struct hid_device *hdev, goto err_mutex; } + /* Enable rumble */ + ret = joycon_enable_rumble(ctlr, true); + if (ret) { + hid_err(hdev, "Failed to enable rumble; ret=%d\n", ret); + goto err_mutex; + } + mutex_unlock(&ctlr->output_mutex); ret = joycon_input_create(ctlr); @@ -1050,6 +1388,8 @@ static int nintendo_hid_probe(struct hid_device *hdev, hid_hw_close(hdev); err_stop: hid_hw_stop(hdev); +err_wq: + destroy_workqueue(ctlr->rumble_queue); err: hid_err(hdev, "probe - fail = %d\n", ret); return ret; @@ -1060,6 +1400,7 @@ static void nintendo_hid_remove(struct hid_device *hdev) struct joycon_ctlr *ctlr = hid_get_drvdata(hdev); hid_dbg(hdev, "remove\n"); + destroy_workqueue(ctlr->rumble_queue); hid_hw_close(hdev); hid_hw_stop(hdev); } From patchwork Wed Nov 6 06:06:44 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Ogorchock X-Patchwork-Id: 11229347 X-Patchwork-Delegate: jikos@jikos.cz Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id ED97A1850 for ; Wed, 6 Nov 2019 06:07:05 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id B5893217D7 for ; Wed, 6 Nov 2019 06:07:05 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="c0VtaimF" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726129AbfKFGHF (ORCPT ); Wed, 6 Nov 2019 01:07:05 -0500 Received: from mail-yw1-f67.google.com ([209.85.161.67]:32969 "EHLO mail-yw1-f67.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729966AbfKFGHF (ORCPT ); Wed, 6 Nov 2019 01:07:05 -0500 Received: by mail-yw1-f67.google.com with SMTP id q140so3997745ywg.0 for ; Tue, 05 Nov 2019 22:07:04 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=VwgkrQc2MBNZEL6w6XYbE0uQdjbohGCU3eWkXwbPbsY=; b=c0VtaimFoQIOCZvhcpnNhsAkXrC5PUbKkRSP9f25NwIfGVbCu1sdVecd+P/2eTQ5Q+ s9JBSgZuL8Hg9cmc5ek91sm8w16CExp4pGheRzUbPcHjQINJTfasAbYajWXsPJ+ZFsxE otYRFkNdIzCK4JsEnuxIyWptZDK2vNklcpGQhoIy6hg8ZJPYpMtEGJC6rnUeIyE5fi4H JVYQiNA+6ie3FzEvlm0DvJsBeWWXygQHYCU0JmavAz8MCmPFQ8PLoJgQ8Ek4Ablf542b yFRW1GAatDqpAJjFsBeGyrRjl/PIyZW6cm1diA3FR7/SpA7CO04/62WoyV/Yspptw3B1 fJOg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=VwgkrQc2MBNZEL6w6XYbE0uQdjbohGCU3eWkXwbPbsY=; b=MNXp4jSVOKTECgV8Q2grz9P33egTMDHR0UiCxMdFs/+YYuR8QtxMLLOxT8HZvYY+SR jDOlSCsySRMFPWiyoyzQk+b9t1aLe5o8wPfcaYyO0olv3rtnr/d1XKT4w+yFmvwNyx7R 1+j4QcSO6Ki490h/hLieIcms+xsV2zOKUrqAurYxcfhidRwPuWuj+cUD17eOCoo8AH4x GrCZbBWMzFIOr/tNJFsP2pELvZDSnIPRohNk/ZCTjn6qmQhtPfLNhAKPRX7cFKuJjybP Q4STi1CPromR42C/Bu24xkBADDPLiS1ABM0otLYoAlsliHoxZmND57DVIKAgpjSZ+Ugr UQ+w== X-Gm-Message-State: APjAAAWt/eJ3V6vXrUQuJRemabz/+FqByRSyNkiv3D/Bm0l9DL3Y8A6o ns7+Tv/Q38qaeQa4nZXtD8x+N7/BPQtJsA== X-Google-Smtp-Source: APXvYqyCrw7/k3ZGVUtXrVnVTg2InXsLCrONvtFnz1ESAMSGLhQELagLMCzProXqvK7/FDuV67A8hA== X-Received: by 2002:a81:1b49:: with SMTP id b70mr453457ywb.124.1573020424045; Tue, 05 Nov 2019 22:07:04 -0800 (PST) Received: from Arrakis.djogorchock.com ([136.33.205.58]) by smtp.gmail.com with ESMTPSA id e82sm11039596ywb.87.2019.11.05.22.07.02 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 05 Nov 2019 22:07:03 -0800 (PST) From: "Daniel J. Ogorchock" To: linux-input@vger.kernel.org Cc: thunderbird2k@gmail.com, blaws05@gmail.com, benjamin.tissoires@redhat.com, jikos@kernel.org, Roderick.Colenbrander@sony.com, svv@google.com, s.jegen@gmail.com, "Daniel J. Ogorchock" Subject: [PATCH v9 06/10] HID: nintendo: improve subcommand reliability Date: Wed, 6 Nov 2019 00:06:44 -0600 Message-Id: <20191106060648.3018478-7-djogorchock@gmail.com> X-Mailer: git-send-email 2.23.0 In-Reply-To: <20191106060648.3018478-1-djogorchock@gmail.com> References: <20191106060648.3018478-1-djogorchock@gmail.com> MIME-Version: 1.0 Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org The controller occasionally doesn't respond to subcommands. It appears that it's dropping them. To improve reliability, this patch attempts one retry in the case of a synchronous send timeout. In testing, this has resolved all timeout failures (most common for LED setting and rumble setting subcommands). The 1 second timeout is excessively long for rumble and LED subcommands, so the timeout has been made a param for joycon_hid_send_sync. Most subcommands continue to use the 1s timeout, since they can result in long response times. Rumble and LED setting subcommands have been reduced to 250ms, since response times for them are much quicker (and this significantly reduces the observable impact in the case of a retry being required). Signed-off-by: Daniel J. Ogorchock --- drivers/hid/hid-nintendo.c | 56 +++++++++++++++++++++++++------------- 1 file changed, 37 insertions(+), 19 deletions(-) diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c index ec632a4b9785..8bd49f3a67ab 100644 --- a/drivers/hid/hid-nintendo.c +++ b/drivers/hid/hid-nintendo.c @@ -365,24 +365,42 @@ static int __joycon_hid_send(struct hid_device *hdev, u8 *data, size_t len) return ret; } -static int joycon_hid_send_sync(struct joycon_ctlr *ctlr, u8 *data, size_t len) +static int joycon_hid_send_sync(struct joycon_ctlr *ctlr, u8 *data, size_t len, + u32 timeout) { int ret; + int tries = 2; - ret = __joycon_hid_send(ctlr->hdev, data, len); - if (ret < 0) { - memset(ctlr->input_buf, 0, JC_MAX_RESP_SIZE); - return ret; - } + /* + * The controller occasionally seems to drop subcommands. In testing, + * doing one retry after a timeout appears to always work. + */ + while (tries--) { + ret = __joycon_hid_send(ctlr->hdev, data, len); + if (ret < 0) { + memset(ctlr->input_buf, 0, JC_MAX_RESP_SIZE); + return ret; + } - if (!wait_event_timeout(ctlr->wait, ctlr->received_resp, HZ)) { - hid_dbg(ctlr->hdev, "synchronous send/receive timed out\n"); - memset(ctlr->input_buf, 0, JC_MAX_RESP_SIZE); - return -ETIMEDOUT; + ret = wait_event_timeout(ctlr->wait, ctlr->received_resp, + timeout); + if (!ret) { + hid_dbg(ctlr->hdev, + "synchronous send/receive timed out\n"); + if (tries) { + hid_dbg(ctlr->hdev, + "retrying sync send after timeout\n"); + } + memset(ctlr->input_buf, 0, JC_MAX_RESP_SIZE); + ret = -ETIMEDOUT; + } else { + ret = 0; + break; + } } ctlr->received_resp = false; - return 0; + return ret; } static int joycon_send_usb(struct joycon_ctlr *ctlr, u8 cmd) @@ -393,7 +411,7 @@ static int joycon_send_usb(struct joycon_ctlr *ctlr, u8 cmd) buf[1] = cmd; ctlr->usb_ack_match = cmd; ctlr->msg_type = JOYCON_MSG_TYPE_USB; - ret = joycon_hid_send_sync(ctlr, buf, sizeof(buf)); + ret = joycon_hid_send_sync(ctlr, buf, sizeof(buf), HZ); if (ret) hid_dbg(ctlr->hdev, "send usb command failed; ret=%d\n", ret); return ret; @@ -401,7 +419,7 @@ static int joycon_send_usb(struct joycon_ctlr *ctlr, u8 cmd) static int joycon_send_subcmd(struct joycon_ctlr *ctlr, struct joycon_subcmd_request *subcmd, - size_t data_len) + size_t data_len, u32 timeout) { int ret; unsigned long flags; @@ -419,7 +437,7 @@ static int joycon_send_subcmd(struct joycon_ctlr *ctlr, ctlr->msg_type = JOYCON_MSG_TYPE_SUBCMD; ret = joycon_hid_send_sync(ctlr, (u8 *)subcmd, - sizeof(*subcmd) + data_len); + sizeof(*subcmd) + data_len, timeout); if (ret < 0) hid_dbg(ctlr->hdev, "send subcommand failed; ret=%d\n", ret); else @@ -438,7 +456,7 @@ static int joycon_set_player_leds(struct joycon_ctlr *ctlr, u8 flash, u8 on) req->data[0] = (flash << 4) | on; hid_dbg(ctlr->hdev, "setting player leds\n"); - return joycon_send_subcmd(ctlr, req, 1); + return joycon_send_subcmd(ctlr, req, 1, HZ/4); } static const u16 DFLT_STICK_CAL_CEN = 2000; @@ -469,7 +487,7 @@ static int joycon_request_calibration(struct joycon_ctlr *ctlr) data[4] = JC_CAL_DATA_SIZE; hid_dbg(ctlr->hdev, "requesting cal data\n"); - ret = joycon_send_subcmd(ctlr, req, 5); + ret = joycon_send_subcmd(ctlr, req, 5, HZ); if (ret) { hid_warn(ctlr->hdev, "Failed to read stick cal, using defaults; ret=%d\n", @@ -559,7 +577,7 @@ static int joycon_set_report_mode(struct joycon_ctlr *ctlr) req->data[0] = 0x30; /* standard, full report mode */ hid_dbg(ctlr->hdev, "setting controller report mode\n"); - return joycon_send_subcmd(ctlr, req, 1); + return joycon_send_subcmd(ctlr, req, 1, HZ); } static int joycon_enable_rumble(struct joycon_ctlr *ctlr, bool enable) @@ -572,7 +590,7 @@ static int joycon_enable_rumble(struct joycon_ctlr *ctlr, bool enable) req->data[0] = enable ? 0x01 : 0x00; hid_dbg(ctlr->hdev, "%s rumble\n", enable ? "enabling" : "disabling"); - return joycon_send_subcmd(ctlr, req, 1); + return joycon_send_subcmd(ctlr, req, 1, HZ/4); } static s32 joycon_map_stick_val(struct joycon_stick_cal *cal, s32 val) @@ -1014,7 +1032,7 @@ static int joycon_home_led_brightness_set(struct led_classdev *led, hid_dbg(hdev, "setting home led brightness\n"); mutex_lock(&ctlr->output_mutex); - ret = joycon_send_subcmd(ctlr, req, 5); + ret = joycon_send_subcmd(ctlr, req, 5, HZ/4); mutex_unlock(&ctlr->output_mutex); return ret; From patchwork Wed Nov 6 06:06:45 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Ogorchock X-Patchwork-Id: 11229351 X-Patchwork-Delegate: jikos@jikos.cz Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 4D8171515 for ; Wed, 6 Nov 2019 06:07:07 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 1F96720663 for ; Wed, 6 Nov 2019 06:07:07 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Hj/xWwnv" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1725812AbfKFGHG (ORCPT ); Wed, 6 Nov 2019 01:07:06 -0500 Received: from mail-yb1-f195.google.com ([209.85.219.195]:41097 "EHLO mail-yb1-f195.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729966AbfKFGHG (ORCPT ); Wed, 6 Nov 2019 01:07:06 -0500 Received: by mail-yb1-f195.google.com with SMTP id b2so10453587ybr.8 for ; Tue, 05 Nov 2019 22:07:06 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=B/yysPScvQxkf0Nqw8IXbXHBz/k48AdxrLpy2edWnUU=; b=Hj/xWwnv6EsE/Qen9yZHZngO6KkHFUN+DOi7mfHnAsj9StUfHD/k8CNYFvigwc70tq 7HzAw+SG7bFz4z/GflsWD0WLAQVzJnWunRMLXdmvfvO70dOn7wFOjwO8SGGA22/WKy/5 hS90VP8YpHv4Y+JNtC75TYqeNaj7ubYM821N0piUUFiXpJPj0Ef/Fk3Q0f0Rdi8pM8NR zGXhGLSTYZmTXnFbm4mzOlBiFRrODiaOeGDyxFuvtQHDDF4aea6XH/Wz3hUq992tHf3W uqddF8AGIE6FOsPqgdAoSar0ked+h58e646SgQjRwfDak73411q5vNiU+23gBs7mDMwC 80GQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=B/yysPScvQxkf0Nqw8IXbXHBz/k48AdxrLpy2edWnUU=; b=heCWNbyMDgRPaCSfng31PmQ+AyO3WMDV/HsSH56O4i/4ywOXMatmklR63r9YsX3Nzk XhfDkWRsebrkysJ+JGLPHXh+YiBkhWu9UKT4SjA1TdCfoY8kxLJdEuMA9JXMjF2KtnYf wsKHurGaW0nyPVJD9bNRVNlrf5EetlprpQBXVuTBkvA1QxY2orERq5N8E0zYynNytZYx DWMERnHgzN/QTLrVxVQB4SnSh7cYpbjJUKTlyygEyoeb1TudlmMjZXVa+ibWmDxqS2TO 4N+z0K1ATpV36i24jweaFkfRBti0rW+sIn/zJz4r8aP5vapKx9T+b+gtuLf/DQR97/0+ u9yQ== X-Gm-Message-State: APjAAAWqCox3m4bVbwbv2FuihHgAxxKwntPe2rouavzTKFPbQcspgrXC JyPp1cD0XvtWcJNItPXCqXMH83wTcGi74g== X-Google-Smtp-Source: APXvYqxvoBqt2TU8wss8UI0h4xRcmr8gsulHXBsnjHs9ReFaCAXdeM12ws9SRiz4b0UCyj8KwCVL2g== X-Received: by 2002:a25:c64b:: with SMTP id k72mr751796ybf.4.1573020425551; Tue, 05 Nov 2019 22:07:05 -0800 (PST) Received: from Arrakis.djogorchock.com ([136.33.205.58]) by smtp.gmail.com with ESMTPSA id e82sm11039596ywb.87.2019.11.05.22.07.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 05 Nov 2019 22:07:04 -0800 (PST) From: "Daniel J. Ogorchock" To: linux-input@vger.kernel.org Cc: thunderbird2k@gmail.com, blaws05@gmail.com, benjamin.tissoires@redhat.com, jikos@kernel.org, Roderick.Colenbrander@sony.com, svv@google.com, s.jegen@gmail.com, "Daniel J. Ogorchock" Subject: [PATCH v9 07/10] HID: nintendo: send subcommands after receiving input report Date: Wed, 6 Nov 2019 00:06:45 -0600 Message-Id: <20191106060648.3018478-8-djogorchock@gmail.com> X-Mailer: git-send-email 2.23.0 In-Reply-To: <20191106060648.3018478-1-djogorchock@gmail.com> References: <20191106060648.3018478-1-djogorchock@gmail.com> MIME-Version: 1.0 Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org Waiting to send subcommands until right after receiving an input report drastically improves subcommand reliability. If the driver has finished initial controller configuration, it now waits until receiving an input report for all subcommands. Signed-off-by: Daniel J. Ogorchock --- drivers/hid/hid-nintendo.c | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c index 8bd49f3a67ab..14ffd188ef78 100644 --- a/drivers/hid/hid-nintendo.c +++ b/drivers/hid/hid-nintendo.c @@ -323,6 +323,7 @@ struct joycon_ctlr { bool received_resp; u8 usb_ack_match; u8 subcmd_ack_match; + bool received_input_report; /* factory calibration data */ struct joycon_stick_cal left_stick_cal_x; @@ -376,6 +377,26 @@ static int joycon_hid_send_sync(struct joycon_ctlr *ctlr, u8 *data, size_t len, * doing one retry after a timeout appears to always work. */ while (tries--) { + /* + * If we are in the proper reporting mode, wait for an input + * report prior to sending the subcommand. This improves + * reliability considerably. + */ + if (ctlr->ctlr_state == JOYCON_CTLR_STATE_READ) { + unsigned long flags; + + spin_lock_irqsave(&ctlr->lock, flags); + ctlr->received_input_report = false; + spin_unlock_irqrestore(&ctlr->lock, flags); + ret = wait_event_timeout(ctlr->wait, + ctlr->received_input_report, + HZ / 4); + /* We will still proceed, even with a timeout here */ + if (!ret) + hid_warn(ctlr->hdev, + "timeout waiting for input report\n"); + } + ret = __joycon_hid_send(ctlr->hdev, data, len); if (ret < 0) { memset(ctlr->input_buf, 0, JC_MAX_RESP_SIZE); @@ -726,6 +747,18 @@ static void joycon_parse_report(struct joycon_ctlr *ctlr, } input_sync(dev); + + /* + * Immediately after receiving a report is the most reliable time to + * send a subcommand to the controller. Wake any subcommand senders + * waiting for a report. + */ + if (unlikely(mutex_is_locked(&ctlr->output_mutex))) { + spin_lock_irqsave(&ctlr->lock, flags); + ctlr->received_input_report = true; + spin_unlock_irqrestore(&ctlr->lock, flags); + wake_up(&ctlr->wait); + } } static void joycon_rumble_worker(struct work_struct *work) From patchwork Wed Nov 6 06:06:46 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Ogorchock X-Patchwork-Id: 11229353 X-Patchwork-Delegate: jikos@jikos.cz Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 821C815AB for ; Wed, 6 Nov 2019 06:07:08 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 555482087E for ; Wed, 6 Nov 2019 06:07:08 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="VSs+pl02" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730192AbfKFGHI (ORCPT ); Wed, 6 Nov 2019 01:07:08 -0500 Received: from mail-yw1-f66.google.com ([209.85.161.66]:45765 "EHLO mail-yw1-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729966AbfKFGHH (ORCPT ); Wed, 6 Nov 2019 01:07:07 -0500 Received: by mail-yw1-f66.google.com with SMTP id j137so3001803ywa.12 for ; Tue, 05 Nov 2019 22:07:07 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=RUUVI4tPjb0488dqXuiXLvB7XNzExNqZK4N7eC+BHN0=; b=VSs+pl02MTiBfd+L4DqKOgdi0GzMKby9A27kHHtpABmbycbWgkHF9OFEeNtQy8DLiv ybnaBNyOkINg44O+J1HQiEeCOBa4s+dNpHDBv25NamkXk3NrrQ5b9KXAzPsdgStZ92ci T7oyDGJdF5zhEfeBBAftP8UpTID98tBAcSpE1wVzxmYBRaIbWeNkWff7gN8xBji23uhi m/buUYu1rPp+9cVm8gKYxE3VqoQ03D9JvLRhK7JqKyklfmryQ1PYSu/x635T6gnjihG6 rj/RZdb+3tdceQ/L/zaRMpkPvhsiBZeX5ORn4XvrHrFoCKKOFeX5cO/o4jZTU0kCtHfK ENnw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=RUUVI4tPjb0488dqXuiXLvB7XNzExNqZK4N7eC+BHN0=; b=WrQrzrmNAiX2r18rmR24qlSuYxSNoLqXapSxoHfa2jtooBz5X7Wc0YMF63kYCfsDcM F972B88OjdWnCVr4IfmnyWDtg+eMOyqwPtG5Z+NuNanbcP5pTRqIB9t5uAfAX50z6KAR P54YbcCm3g9oFSXbFh7HaVPSA+gcCc3ckQVqpuu9JTatvzOlV/r+/Q2Jwk2/OJpMpf2B kJj0qzKyaWSxML2xmdRXDPcl6SYNEc3YMQPT2BZYpt9G+60tyy/++3jEv7NZbEYazNF7 zUQI8ySYH/zMDfnTEN0yLxu37+HYdb0rwvvPCHhe0n5J7sByy1Jo9BsxoTIteONGQzHL Ta+g== X-Gm-Message-State: APjAAAUS68GqmFAcf9hOFaSLH0wF1KSVKTNLIiycRI/zWxgZaTfkskzp oulaCU9SPRN6dNFWqstZ2jP+BkmCzeD4cA== X-Google-Smtp-Source: APXvYqxZ2LMeu1ZT5S8+HGMKPVDYlW3hFpi95Ks1ghcygZXaAY0RRYKSZsSWwQ7AobxP+3uL5WCbHQ== X-Received: by 2002:a81:1e54:: with SMTP id e81mr418487ywe.437.1573020426774; Tue, 05 Nov 2019 22:07:06 -0800 (PST) Received: from Arrakis.djogorchock.com ([136.33.205.58]) by smtp.gmail.com with ESMTPSA id e82sm11039596ywb.87.2019.11.05.22.07.05 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 05 Nov 2019 22:07:06 -0800 (PST) From: "Daniel J. Ogorchock" To: linux-input@vger.kernel.org Cc: thunderbird2k@gmail.com, blaws05@gmail.com, benjamin.tissoires@redhat.com, jikos@kernel.org, Roderick.Colenbrander@sony.com, svv@google.com, s.jegen@gmail.com, "Daniel J. Ogorchock" Subject: [PATCH v9 08/10] HID: nintendo: reduce device removal subcommand errors Date: Wed, 6 Nov 2019 00:06:46 -0600 Message-Id: <20191106060648.3018478-9-djogorchock@gmail.com> X-Mailer: git-send-email 2.23.0 In-Reply-To: <20191106060648.3018478-1-djogorchock@gmail.com> References: <20191106060648.3018478-1-djogorchock@gmail.com> MIME-Version: 1.0 Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org This patch fixes meaningless error output from trying to send subcommands immediately after controller removal. It now disables subcommands as soon as possible on removal. Signed-off-by: Daniel J. Ogorchock --- drivers/hid/hid-nintendo.c | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c index 14ffd188ef78..074fd59decbe 100644 --- a/drivers/hid/hid-nintendo.c +++ b/drivers/hid/hid-nintendo.c @@ -324,6 +324,7 @@ struct joycon_ctlr { u8 usb_ack_match; u8 subcmd_ack_match; bool received_input_report; + bool ctlr_removed; /* factory calibration data */ struct joycon_stick_cal left_stick_cal_x; @@ -391,10 +392,12 @@ static int joycon_hid_send_sync(struct joycon_ctlr *ctlr, u8 *data, size_t len, ret = wait_event_timeout(ctlr->wait, ctlr->received_input_report, HZ / 4); + spin_lock_irqsave(&ctlr->lock, flags); /* We will still proceed, even with a timeout here */ - if (!ret) + if (!ret && !ctlr->ctlr_removed) hid_warn(ctlr->hdev, "timeout waiting for input report\n"); + spin_unlock_irqrestore(&ctlr->lock, flags); } ret = __joycon_hid_send(ctlr->hdev, data, len); @@ -446,6 +449,14 @@ static int joycon_send_subcmd(struct joycon_ctlr *ctlr, unsigned long flags; spin_lock_irqsave(&ctlr->lock, flags); + /* + * If the controller has been removed, just return ENODEV so the LED + * subsystem doesn't print invalid errors on removal. + */ + if (ctlr->ctlr_removed) { + spin_unlock_irqrestore(&ctlr->lock, flags); + return -ENODEV; + } memcpy(subcmd->rumble_data, ctlr->rumble_data[ctlr->rumble_queue_tail], JC_RUMBLE_DATA_SIZE); spin_unlock_irqrestore(&ctlr->lock, flags); @@ -773,10 +784,12 @@ static void joycon_rumble_worker(struct work_struct *work) mutex_lock(&ctlr->output_mutex); ret = joycon_enable_rumble(ctlr, true); mutex_unlock(&ctlr->output_mutex); - if (ret < 0) - hid_warn(ctlr->hdev, "Failed to set rumble; e=%d", ret); + /* -ENODEV means the controller was just unplugged */ spin_lock_irqsave(&ctlr->lock, flags); + if (ret < 0 && ret != -ENODEV && !ctlr->ctlr_removed) + hid_warn(ctlr->hdev, "Failed to set rumble; e=%d", ret); + ctlr->rumble_msecs = jiffies_to_msecs(jiffies); if (ctlr->rumble_queue_tail != ctlr->rumble_queue_head) { if (++ctlr->rumble_queue_tail >= JC_RUMBLE_QUEUE_SIZE) @@ -1449,9 +1462,17 @@ static int nintendo_hid_probe(struct hid_device *hdev, static void nintendo_hid_remove(struct hid_device *hdev) { struct joycon_ctlr *ctlr = hid_get_drvdata(hdev); + unsigned long flags; hid_dbg(hdev, "remove\n"); + + /* Prevent further attempts at sending subcommands. */ + spin_lock_irqsave(&ctlr->lock, flags); + ctlr->ctlr_removed = true; + spin_unlock_irqrestore(&ctlr->lock, flags); + destroy_workqueue(ctlr->rumble_queue); + hid_hw_close(hdev); hid_hw_stop(hdev); } From patchwork Wed Nov 6 06:06:47 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Ogorchock X-Patchwork-Id: 11229355 X-Patchwork-Delegate: jikos@jikos.cz Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id D5CAB15AB for ; Wed, 6 Nov 2019 06:07:09 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id A82BE214D8 for ; Wed, 6 Nov 2019 06:07:09 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="tSihw4Yn" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730272AbfKFGHJ (ORCPT ); Wed, 6 Nov 2019 01:07:09 -0500 Received: from mail-yw1-f65.google.com ([209.85.161.65]:40346 "EHLO mail-yw1-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729966AbfKFGHJ (ORCPT ); Wed, 6 Nov 2019 01:07:09 -0500 Received: by mail-yw1-f65.google.com with SMTP id a67so9216725ywg.7 for ; Tue, 05 Nov 2019 22:07:08 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=SmFjnAIFh2gUqdvKRpTCd7aeEiOY9CdSh1BQkzZ/c3c=; b=tSihw4YnCDL7JjmklsFJlGA8L2zScTcLFXDY2WYgFdDX4IUuPwb6DC7EAC4eCkLuLX EZmkATgaY7+gutKLXW2saLyogSPUQAt8/Z4KD3mLloamDSlufo2ACpqnfGMAh168Mvlh pomHlPnJ9d0A11ajpedKko5yByoYBwsfLuW1BFrtFHJUeXwJqhQKteFRQDkynnhdB1zU ZDflvtcz/Fa8aWyeexyKa/0RXPZWxvGpysZSBVs6ANnvdhhratt477EyUFsDVhmt2+LY jtk/Yrt/zDeCc3zgB7R6oVCu1Os1l9CSQOyG8Yjv5pxcQ52uPpDmIfhHz6JRxOqOWLW1 3Z6w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=SmFjnAIFh2gUqdvKRpTCd7aeEiOY9CdSh1BQkzZ/c3c=; b=HzDqXxrybJvrgd3+tEnL7b1t5mahiDtMp74kaD3O/CtOS1Zh1s5tyk11ua4Bj8zQWA 2yXTQZOIId7tFPTS45uJ5+wxoD0GlNPsX5PqwXO8CxB8TiSeTqDQmX6mnO+iMzmOcx0q FU9QBJq9LrPqOE5PP4ub4yVdYqomGYhfxpC1aA0cGMHnyMeP6pICPaJpsLNy1bX2UJJO Aw51Xssl4nauwCA25Bm18CML3D4C2O4rvZdJN0VkXtG0KwQs/7wK3ecxDiW6quWDKJ+i xK2qG+G704H9aXkGJR0GRYcjqjXDD+JPe4uyItnJ/pnw3cumz+ucBa12AbrAk9f95rh6 XZow== X-Gm-Message-State: APjAAAWwAwhM30gg8q9P+aDv4vSQDsZg7mxV4foL01CfBA13D8doTYMf WVUoSNkbOAMq/Pvh5WnQBsMVzo1D25gIqA== X-Google-Smtp-Source: APXvYqz7HHttA/JWVBe96MA0cW7MahviepkdWZ0lGeEEUfOkt4VIwBt2On5O0LsMAl73qE0IYjbsvg== X-Received: by 2002:a81:5745:: with SMTP id l66mr485741ywb.70.1573020428092; Tue, 05 Nov 2019 22:07:08 -0800 (PST) Received: from Arrakis.djogorchock.com ([136.33.205.58]) by smtp.gmail.com with ESMTPSA id e82sm11039596ywb.87.2019.11.05.22.07.06 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 05 Nov 2019 22:07:07 -0800 (PST) From: "Daniel J. Ogorchock" To: linux-input@vger.kernel.org Cc: thunderbird2k@gmail.com, blaws05@gmail.com, benjamin.tissoires@redhat.com, jikos@kernel.org, Roderick.Colenbrander@sony.com, svv@google.com, s.jegen@gmail.com, "Daniel J. Ogorchock" Subject: [PATCH v9 09/10] HID: nintendo: patch hw version for userspace HID mappings Date: Wed, 6 Nov 2019 00:06:47 -0600 Message-Id: <20191106060648.3018478-10-djogorchock@gmail.com> X-Mailer: git-send-email 2.23.0 In-Reply-To: <20191106060648.3018478-1-djogorchock@gmail.com> References: <20191106060648.3018478-1-djogorchock@gmail.com> MIME-Version: 1.0 Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org This patch sets the most significant bit of the hid hw version to allow userspace to distinguish between this driver's input mappings vs. the default hid mappings. This prevents breaking userspace applications that use SDL2 for gamepad input, allowing them to distinguish the mappings based on the version. Signed-off-by: Daniel J. Ogorchock --- drivers/hid/hid-nintendo.c | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c index 074fd59decbe..24c4ec4e453a 100644 --- a/drivers/hid/hid-nintendo.c +++ b/drivers/hid/hid-nintendo.c @@ -1356,6 +1356,15 @@ static int nintendo_hid_probe(struct hid_device *hdev, goto err_wq; } + /* + * Patch the hw version of pro controller/joycons, so applications can + * distinguish between the default HID mappings and the mappings defined + * by the Linux game controller spec. This is important for the SDL2 + * library, which has a game controller database, which uses device ids + * in combination with version as a key. + */ + hdev->version |= 0x8000; + ret = hid_hw_start(hdev, HID_CONNECT_HIDRAW); if (ret) { hid_err(hdev, "HW start failed\n"); From patchwork Wed Nov 6 06:06:48 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Ogorchock X-Patchwork-Id: 11229357 X-Patchwork-Delegate: jikos@jikos.cz Return-Path: Received: from mail.kernel.org (pdx-korg-mail-1.web.codeaurora.org [172.30.200.123]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 539D51515 for ; Wed, 6 Nov 2019 06:07:12 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 24FBD20663 for ; Wed, 6 Nov 2019 06:07:12 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="WtVhmZxD" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1730037AbfKFGHL (ORCPT ); Wed, 6 Nov 2019 01:07:11 -0500 Received: from mail-yw1-f66.google.com ([209.85.161.66]:44515 "EHLO mail-yw1-f66.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729966AbfKFGHL (ORCPT ); Wed, 6 Nov 2019 01:07:11 -0500 Received: by mail-yw1-f66.google.com with SMTP id p128so353792ywc.11 for ; Tue, 05 Nov 2019 22:07:10 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references :mime-version:content-transfer-encoding; bh=ow9f9V+rSKoz1ZYone9J08Jkn/N4+iISXil9NZ29mb0=; b=WtVhmZxDPRrNwV0eCRXPXdquZOSVixoit2ADKcc3k8WwYA2nhjtMUR/F/3QttERRSQ 0qVysrrKP2g80B5kGo1Wtw3upTYDVEC8E5p+Akq9F1E6+jyN1JGs5NcjpJMSVyWOy+fQ NtykjqVdhJu3JTXB7w4Vmm5QS3ckueipjpT90dSxXO4E105BsJoKZIRt+NyXXk9pWkUh h87ZPjfAMcUJgDwIT85u2QISISkI/2n09gk/22tZjBSYn2oGZ+5uM1IMgCyW69JmD2V8 QAqYPirR/d0ON/WT2We39QdI65K880AC5dsvxF4UDNA5ycdEH62SOdloex7oeUlPPe4J HZnQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=ow9f9V+rSKoz1ZYone9J08Jkn/N4+iISXil9NZ29mb0=; b=lGLNZoVBW4m7zRs7VN+00yuVhGQd5Tuj1pJS4VZ99a6ZkU2QNw8XVG00Ib+nxcLk/c 7UFbAEboFenL/opIO/A/9t84nQnKUYFLFEGy2GKe1cmEqw8Q1fLTOLWvoPfTuNzjsaf/ eEJFDr7xXMiZL+9ua7jQ0R1DI4oskuXPtNzLxrYgPMZhKn+fCSKj6x2SPi/LRfSJyGn/ sDC3wXz6SHJcFSam+xdBsso54OKI57pTUG9M6JwZNn/y2rRKyPFYDpRnveaigrIIL7LA jTUA6PB/rIyu5T9galrD9A+Eis2TLKDjK6VUFaOYcO3PZSYkVDhNIo6+Be2yBVBB+B15 WHfA== X-Gm-Message-State: APjAAAXWneOi2SlPal3ayfFiaAZHeGRVLzs/PSfAlm39Huar9HtNo4Ui tmg+6uiwR/zRy7ki9WPreXh0qOfv8sSZXQ== X-Google-Smtp-Source: APXvYqzDOnxBUwfmeQXjY6NgATEfkbtY73Z6YgC7U6/r4bMe+FbMLUVNnvCn5nrSh3/yG7ExCRUCrg== X-Received: by 2002:a81:b617:: with SMTP id u23mr435292ywh.295.1573020429284; Tue, 05 Nov 2019 22:07:09 -0800 (PST) Received: from Arrakis.djogorchock.com ([136.33.205.58]) by smtp.gmail.com with ESMTPSA id e82sm11039596ywb.87.2019.11.05.22.07.08 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 05 Nov 2019 22:07:08 -0800 (PST) From: "Daniel J. Ogorchock" To: linux-input@vger.kernel.org Cc: thunderbird2k@gmail.com, blaws05@gmail.com, benjamin.tissoires@redhat.com, jikos@kernel.org, Roderick.Colenbrander@sony.com, svv@google.com, s.jegen@gmail.com, "Daniel J. Ogorchock" Subject: [PATCH v9 10/10] HID: nintendo: set controller uniq to MAC Date: Wed, 6 Nov 2019 00:06:48 -0600 Message-Id: <20191106060648.3018478-11-djogorchock@gmail.com> X-Mailer: git-send-email 2.23.0 In-Reply-To: <20191106060648.3018478-1-djogorchock@gmail.com> References: <20191106060648.3018478-1-djogorchock@gmail.com> MIME-Version: 1.0 Sender: linux-input-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-input@vger.kernel.org This patch sets the input device's uniq identifier to the controller's MAC address. This is useful for future association between an IMU input device with the normal input device as well as associating the controller with any serial joy-con driver. Signed-off-by: Daniel J. Ogorchock --- drivers/hid/hid-nintendo.c | 45 ++++++++++++++++++++++++++++++++++++++ 1 file changed, 45 insertions(+) diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c index 24c4ec4e453a..c81bdcda76d4 100644 --- a/drivers/hid/hid-nintendo.c +++ b/drivers/hid/hid-nintendo.c @@ -313,6 +313,8 @@ struct joycon_ctlr { struct led_classdev home_led; enum joycon_ctlr_state ctlr_state; spinlock_t lock; + u8 mac_addr[6]; + char *mac_addr_str; /* The following members are used for synchronous sends/receives */ enum joycon_msg_type msg_type; @@ -970,6 +972,7 @@ static int joycon_input_create(struct joycon_ctlr *ctlr) ctlr->input->id.vendor = hdev->vendor; ctlr->input->id.product = hdev->product; ctlr->input->id.version = hdev->version; + ctlr->input->uniq = ctlr->mac_addr_str; ctlr->input->name = name; input_set_drvdata(ctlr->input, ctlr); @@ -1250,6 +1253,41 @@ static int joycon_power_supply_create(struct joycon_ctlr *ctlr) return 0; } +static int joycon_read_mac(struct joycon_ctlr *ctlr) +{ + int ret; + int i; + int j; + struct joycon_subcmd_request req = { 0 }; + struct joycon_input_report *report; + + req.subcmd_id = JC_SUBCMD_REQ_DEV_INFO; + ret = joycon_send_subcmd(ctlr, &req, 0, HZ); + if (ret) { + hid_err(ctlr->hdev, "Failed to get joycon info; ret=%d\n", ret); + return ret; + } + + report = (struct joycon_input_report *)ctlr->input_buf; + + for (i = 4, j = 0; j < 6; i++, j++) + ctlr->mac_addr[j] = report->reply.data[i]; + + ctlr->mac_addr_str = devm_kasprintf(&ctlr->hdev->dev, GFP_KERNEL, + "%02X:%02X:%02X:%02X:%02X:%02X", + ctlr->mac_addr[0], + ctlr->mac_addr[1], + ctlr->mac_addr[2], + ctlr->mac_addr[3], + ctlr->mac_addr[4], + ctlr->mac_addr[5]); + if (!ctlr->mac_addr_str) + return -ENOMEM; + hid_info(ctlr->hdev, "controller MAC = %s\n", ctlr->mac_addr_str); + + return 0; +} + /* Common handler for parsing inputs */ static int joycon_ctlr_read_handler(struct joycon_ctlr *ctlr, u8 *data, int size) @@ -1428,6 +1466,13 @@ static int nintendo_hid_probe(struct hid_device *hdev, goto err_mutex; } + ret = joycon_read_mac(ctlr); + if (ret) { + hid_err(hdev, "Failed to retrieve controller MAC; ret=%d\n", + ret); + goto err_close; + } + mutex_unlock(&ctlr->output_mutex); ret = joycon_input_create(ctlr);