From patchwork Tue Mar 17 03:29:19 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Ogorchock X-Patchwork-Id: 208091 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.8 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 41353C10F29 for ; Tue, 17 Mar 2020 03:30:02 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 141A42071C for ; Tue, 17 Mar 2020 03:30:02 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="Ix5WsiHU" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726157AbgCQDaB (ORCPT ); Mon, 16 Mar 2020 23:30:01 -0400 Received: from mail-qt1-f196.google.com ([209.85.160.196]:43970 "EHLO mail-qt1-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726130AbgCQDaB (ORCPT ); Mon, 16 Mar 2020 23:30:01 -0400 Received: by mail-qt1-f196.google.com with SMTP id l13so16275209qtv.10 for ; Mon, 16 Mar 2020 20:29:59 -0700 (PDT) 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=lbp8TiNGqHfqKWhtoWJxUoBmzYjXOmhDPwt7lpRSlzE=; b=Ix5WsiHUMmZuMa9fHrWdvvo4KlTY6A9K0VVJHGd9LZ31A4QjfUEHRYKalwD7KaSKXc mflcBVk/3HPkVt810h3fydw7AqPr+eP2vLPBC5uYQ7Q/LtpCGutOgWf/uS7UqjFVwJO6 p4a3ZqgEuCLftp8Mm6o9nR/aptOSrwBzR5GxptHqhuyFh6KVLLJbNwYPDO+P42lXM9ca 45b82H/9+nPMBbZtb5PTa670fnyqn38apkT0CVt0j+metHu29iHmjAJWljv6gAEvQGPH sNT2rf93AU7nb10hWHuBQVQXBjuCeTUdUtjNa9+IrgAyL76CleYp0WBsXxDgBkncgY3L ZKag== 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=lbp8TiNGqHfqKWhtoWJxUoBmzYjXOmhDPwt7lpRSlzE=; b=jXjx57cLr/kzN8v0HrPjafTxIQXTV9H+VrU3BcK+hucVL5TqiGOSpCyYyabRslgRFU T+Os1L9ub7P0kJ4moBgNhgmGeTtL0zpBr2ITOrYu75zpxRI0yjv99z7zXZp8HctvE7Wd PFVtG58oy9v04DythQXTn/nutxiRuRld19fqwD+52AlP2L+xPSK8yuq9+ELm+holX4QT 95ZDNGC2pJkJ82EupetGkGt04hoiAgbBvEwH2+MwVCe+M4EjS9LaYbQM3bAtIQHf9i3Z coSIfqJmlCfdPVoBylKrR7VAYSKgD9Lsk/SLosns8fuWzqVuwjvJcxzAS95PMUpU/G14 BRZw== X-Gm-Message-State: ANhLgQ0lQx60Il5uuB7qmH/dhQUngzyBMYAwuyZ9Q+4OaEYFYLMvuC5t qIRNNQahKAJKwScg9aH/QUEeaSboqlg= X-Google-Smtp-Source: ADFU+vtIsJKFJ0ThWep6bKcJ/BgLAWk0y0XJ9lf9px04Ka/HpBc7gAWNA9309LCiNTMptC4rbtxm9Q== X-Received: by 2002:aed:3b4c:: with SMTP id q12mr3373022qte.18.1584415798670; Mon, 16 Mar 2020 20:29:58 -0700 (PDT) Received: from Arrakis.djogorchock.com ([136.33.205.58]) by smtp.gmail.com with ESMTPSA id t2sm1272264qtp.13.2020.03.16.20.29.57 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 16 Mar 2020 20:29:58 -0700 (PDT) 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, carmueller@gmail.com, "Daniel J. Ogorchock" Subject: [PATCH v11 02/11] HID: nintendo: add player led support Date: Mon, 16 Mar 2020 22:29:19 -0500 Message-Id: <20200317032928.546172-3-djogorchock@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200317032928.546172-1-djogorchock@gmail.com> References: <20200317032928.546172-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 5dbee5fd69df..275f61427eea 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -705,6 +705,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 7273ddf033e2..c3eec9b7c99c 100644 --- a/drivers/hid/hid-nintendo.c +++ b/drivers/hid/hid-nintendo.c @@ -25,6 +25,7 @@ #include #include #include +#include #include #include @@ -183,11 +184,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 */ @@ -553,11 +556,9 @@ static const unsigned int joycon_dpad_inputs_jc[] = { BTN_DPAD_UP, BTN_DPAD_DOWN, BTN_DPAD_LEFT, BTN_DPAD_RIGHT, }; -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; @@ -643,6 +644,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); @@ -650,6 +711,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); @@ -815,6 +899,13 @@ static int nintendo_hid_probe(struct hid_device *hdev, mutex_unlock(&ctlr->output_mutex); + /* 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; + } + ret = joycon_input_create(ctlr); if (ret) { hid_err(hdev, "Failed to create input device; ret=%d\n", ret); From patchwork Tue Mar 17 03:29:20 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Ogorchock X-Patchwork-Id: 208090 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-14.8 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI, MENTIONS_GIT_HOSTING, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 114E3C3F2CE for ; Tue, 17 Mar 2020 03:30:03 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id DB04220724 for ; Tue, 17 Mar 2020 03:30:02 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="cJmBoOPc" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726187AbgCQDaC (ORCPT ); Mon, 16 Mar 2020 23:30:02 -0400 Received: from mail-qv1-f65.google.com ([209.85.219.65]:38724 "EHLO mail-qv1-f65.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726132AbgCQDaC (ORCPT ); Mon, 16 Mar 2020 23:30:02 -0400 Received: by mail-qv1-f65.google.com with SMTP id p60so10143454qva.5 for ; Mon, 16 Mar 2020 20:30:01 -0700 (PDT) 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=CTUd7R2oLlrqbb69NVuAIKpcB/FDM7wmRhPhih2+Jlc=; b=cJmBoOPcGIDgEbXcuh2aHV0ld8fXv2FscftVZtoxp4sX6DGRaEZe0r3JCJYUT26hrd AdrTKLLYeCqSoIFVeSTk9BY6qlLAIg4lUIUpqeHtV/OyfsMhZM7hqw1jfKvuEhfNQNl+ Y9dfA6fGYDdpflhDAeZIytYwY8cuNjLs2umRU5kWObGGAL50b7Lxnx/mVKCGcI3MJYpd ZJomWgowc+OW8otK95cMIZfOBTR19tYolPu9QghWt6sNHjqF0NTehcWvZn3dj/Ba+1gm K8JZNFpTdLl8OO5mwhfDfjTjM23wkasYHcFo/D+S8CKDwzKzJJzTW8WLo8BtOFBKIU+h 9Njw== 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=CTUd7R2oLlrqbb69NVuAIKpcB/FDM7wmRhPhih2+Jlc=; b=TIEynzBYsNqTjXZ5v1CmiU89ZuFpUndovGAdQL92C8VP20Wt0xrN4+U9IK5PI/QlHJ 3EK79KQ/rRRuO18BvUdWrcZbxw1EDv2z9Z42Hqtjltdxi9qfvPA/FoJW+KHGajkq8LtH 5PZlRgAN89pJ6Y+P35X7De4kIn2ZytbrjcXkUHK/9UIP/Iu4Uhou1luhTifOwI5Ic7NY eS/CpRq1v9cnW2hvfl/rW2W/AER/mSmdpCq5AKVNfz7OLTsrayjn3UpZD2Sv52F2rtXV 9buYMB7oa/o7dS+kun655isjHG5S5qU05DYIzTi3nyj52icbfG38TT/W2fVSj9ERSkj2 0k/w== X-Gm-Message-State: ANhLgQ1ZQSIMpOgoK9JYKKCWbrYMJlNHPODFos9n34Cg4LqSepqlEeDs J1MoeSz9xx8GnKHJ6zuDfP7VHBIvHz8= X-Google-Smtp-Source: ADFU+vvLqrOxjpSN1l8EcGNzdMwpymib5I7a0P0n0xSsW+F6TD/TkthCiymkkNnqzhVqNCdhw2V6MQ== X-Received: by 2002:ad4:4c81:: with SMTP id bs1mr3056288qvb.2.1584415800041; Mon, 16 Mar 2020 20:30:00 -0700 (PDT) Received: from Arrakis.djogorchock.com ([136.33.205.58]) by smtp.gmail.com with ESMTPSA id t2sm1272264qtp.13.2020.03.16.20.29.58 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 16 Mar 2020 20:29:59 -0700 (PDT) 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, carmueller@gmail.com, "Daniel J. Ogorchock" Subject: [PATCH v11 03/11] HID: nintendo: add power supply support Date: Mon, 16 Mar 2020 22:29:20 -0500 Message-Id: <20200317032928.546172-4-djogorchock@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200317032928.546172-1-djogorchock@gmail.com> References: <20200317032928.546172-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 275f61427eea..bc401da4ea76 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -707,6 +707,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 c3eec9b7c99c..bbce15056c2d 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 /* @@ -192,6 +194,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; @@ -209,6 +212,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) @@ -439,9 +448,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) { @@ -741,6 +782,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) @@ -834,6 +959,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) { @@ -906,6 +1032,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; + } + ret = joycon_input_create(ctlr); if (ret) { hid_err(hdev, "Failed to create input device; ret=%d\n", ret); From patchwork Tue Mar 17 03:29:22 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Ogorchock X-Patchwork-Id: 208088 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-14.8 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS,INCLUDES_PATCH,MAILING_LIST_MULTI, MENTIONS_GIT_HOSTING, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id EDE93C10F29 for ; Tue, 17 Mar 2020 03:30:07 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id B51E220717 for ; Tue, 17 Mar 2020 03:30:07 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="APMhMjer" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726192AbgCQDaH (ORCPT ); Mon, 16 Mar 2020 23:30:07 -0400 Received: from mail-qk1-f196.google.com ([209.85.222.196]:40326 "EHLO mail-qk1-f196.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1725783AbgCQDaH (ORCPT ); Mon, 16 Mar 2020 23:30:07 -0400 Received: by mail-qk1-f196.google.com with SMTP id j2so17365061qkl.7 for ; Mon, 16 Mar 2020 20:30:05 -0700 (PDT) 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=ghhtjXFIBLok5OpxAQlz2RCq2RcK0OrSRIm1JyvOUv8=; b=APMhMjerfylAiIxDLVfh9eqTovhG3L1qYKzjU40gkQrhHqKOyk6WJBVPM1IJ547iB4 CeCAvGFO2NwWdGpWjRlrY3r69u13YwfYLXlFAzhO3wBLkvYW4LIwmNPx/yD1RcGlhKWc nxeNwl2AU1w6puvdR74S/1C/B3le7VbTFWbFvrt7//lQf9QsDqhxXo/435gZn2AZ6Rvj g9py7xcc+Frzc0Xnn0GoSCastnm6o3V1ApzFwLyfrsvFM6JQifWsmBhuZrUqubU9kQTJ jpeHADlMFg+cSou6WgzglxQ0znG84B/w0ColEprR14sBdLyhsk+/1naHNC75OLAJzV16 yXqg== 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=ghhtjXFIBLok5OpxAQlz2RCq2RcK0OrSRIm1JyvOUv8=; b=O8ookl8jXLowLN7SB02C+3ra7zhCFUUjyEipycPY7r7ARlnuDMKnDWI+Gx/yYHX9f8 exzvkrGMGZzJQCx0/bY9E2G46Ai7rBaDVNSD6SwEb4m4r0VWeZxezRmU28CWdGjupY9y 8zHzCFyy6HgsYGQHEBDIMWrzrofL2sYsABOQgeY5rWhaHjNddqJQNL+j1LxughqGGCOm apzXU7ZYsDpgitiDZ+kSOF7WKUJ2GHRzbViPIP57FmuBJaiLFby6xJPJGIdEvk90IaMJ 0YkM/SiV4YHd8d9KJ1WauciErmkMbkdO48ZAzIg3DQkXs2aWgyB9okhI9wdyQxG+pp18 7GPQ== X-Gm-Message-State: ANhLgQ1suaTFQBMBbKxhwWG1MY1vfYpYQRw9tfBdOMlESnKYnvv8QoWS au3MP3qPy2R1wShyFQzwa4eEnNaNoMw= X-Google-Smtp-Source: ADFU+vtSIBqEnMzT6Jwyz6REIHaL2b7BF/G8AIWWgwFXoEIxg/ncQTx40euaACItwvKnR/LwHO5+8g== X-Received: by 2002:a05:620a:31b:: with SMTP id s27mr2811147qkm.95.1584415802806; Mon, 16 Mar 2020 20:30:02 -0700 (PDT) Received: from Arrakis.djogorchock.com ([136.33.205.58]) by smtp.gmail.com with ESMTPSA id t2sm1272264qtp.13.2020.03.16.20.30.01 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 16 Mar 2020 20:30:02 -0700 (PDT) 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, carmueller@gmail.com, "Daniel J. Ogorchock" Subject: [PATCH v11 05/11] HID: nintendo: add rumble support Date: Mon, 16 Mar 2020 22:29:22 -0500 Message-Id: <20200317032928.546172-6-djogorchock@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200317032928.546172-1-djogorchock@gmail.com> References: <20200317032928.546172-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 | 349 ++++++++++++++++++++++++++++++++++++- 2 files changed, 356 insertions(+), 3 deletions(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index bc401da4ea76..8f0203861fa5 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -716,6 +716,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 18482f2fe0bf..50242a0b6296 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 @@ -110,6 +112,120 @@ static const u16 JC_MAX_DPAD_MAG = 1; static const u16 JC_DPAD_FUZZ /*= 0*/; static const u16 JC_DPAD_FLAT /*= 0*/; +/* 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, @@ -187,6 +303,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 { @@ -219,6 +341,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) @@ -275,6 +409,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; @@ -427,6 +567,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; @@ -453,10 +606,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; @@ -579,6 +737,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_l[] = { BTN_SELECT, BTN_Z, BTN_THUMBL, @@ -682,6 +995,19 @@ static int joycon_input_create(struct joycon_ctlr *ctlr) input_set_capability(ctlr->input, EV_KEY, BTN_TL2); } +#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; @@ -1017,21 +1343,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); @@ -1084,6 +1415,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); /* Initialize the leds */ @@ -1117,6 +1455,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; @@ -1124,7 +1464,10 @@ 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); + hid_dbg(hdev, "remove\n"); + destroy_workqueue(ctlr->rumble_queue); hid_hw_close(hdev); hid_hw_stop(hdev); } From patchwork Tue Mar 17 03:29:24 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Ogorchock X-Patchwork-Id: 208089 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.8 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id B1C16C18E5B for ; Tue, 17 Mar 2020 03:30:07 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 7DAA020674 for ; Tue, 17 Mar 2020 03:30:07 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="HaMgVoj6" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726222AbgCQDaH (ORCPT ); Mon, 16 Mar 2020 23:30:07 -0400 Received: from mail-qk1-f194.google.com ([209.85.222.194]:33777 "EHLO mail-qk1-f194.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726192AbgCQDaG (ORCPT ); Mon, 16 Mar 2020 23:30:06 -0400 Received: by mail-qk1-f194.google.com with SMTP id q17so5571993qki.0 for ; Mon, 16 Mar 2020 20:30:06 -0700 (PDT) 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=LMJ6lOJDnC5LdR8tL831SC3Pwn85DmO9Vw2+wVHOXhY=; b=HaMgVoj6CixK/fiRARinTvZKL13++Gx7v7/wsUhGVTPfAzeouoyGYJeiK3WTlwwD+c s/QmGhSm6hxH7RkUl1qLaASZ7IcWAlOHvg835vtRaN940HspYbiWr/CkXAOWnObIA6QM x2Oo9X7GE0vbW64MFlkc+TRcESScjMriA3TGTb4m3dphqp9K+nWjH2/UqMFOLKxqQwmp uuhNEOZbTAP5r3SsLEdngBlSYeiLzwqy+fq/M4QUW9BwENSkntFklB5SXkRPMzM214MV uGlfMv2j9FQQd5TeZXMgYY0SoCr5dk6FvyjwI/EvCcHG4hZxr7Gk7/l8/bzCJ71H1HTS hZ4w== 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=LMJ6lOJDnC5LdR8tL831SC3Pwn85DmO9Vw2+wVHOXhY=; b=QZn8T89XKe22M+uzR54tVvg1h4g5+v7SIzF5vN8YBWQYkkmjtS6XxxziVpMGrvT4UN qFeAJZSxwq5Wjr00ytnwCdRFKbf/yd4jSobESBGoDIyzFI1gGeeTGfIzih4KSxt0V8Zi 7sSjpsFSoQD6xVSlz20LgSSMlZVvspov/4d0IiPGM5Za8HpA3uVolSfAUACj3HsTdBbP 9HOziy2XeZhrFNaMWDsz63tnPvaPyCnw4OFJ0RUe+A2Lqa82ZHLPRV9MCFNfjJpTl2LW sPNePdZtl9qwNup2a7gEA/oHmFlwprneLtpowPoR2TqlGb9Ox7opG0QguWkqftu04AwW GM2Q== X-Gm-Message-State: ANhLgQ1FlO3EvohyW8Ngf9h3aHVby08O2U+R0dFFuPCsnZ0lDyr9sN9z 7gz4U4a+C+lFNGa41DR/Dkg9SdUaH2U= X-Google-Smtp-Source: ADFU+vsUc8nGAGz4ZpvB/aNbdbdMcWGIf8VznSPT7rfrDFUWDX7zY5imw3TWltfdL0YPnWALtLNuPA== X-Received: by 2002:ae9:e8c6:: with SMTP id a189mr2867902qkg.361.1584415805683; Mon, 16 Mar 2020 20:30:05 -0700 (PDT) Received: from Arrakis.djogorchock.com ([136.33.205.58]) by smtp.gmail.com with ESMTPSA id t2sm1272264qtp.13.2020.03.16.20.30.04 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 16 Mar 2020 20:30:04 -0700 (PDT) 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, carmueller@gmail.com, "Daniel J. Ogorchock" Subject: [PATCH v11 07/11] HID: nintendo: send subcommands after receiving input report Date: Mon, 16 Mar 2020 22:29:24 -0500 Message-Id: <20200317032928.546172-8-djogorchock@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200317032928.546172-1-djogorchock@gmail.com> References: <20200317032928.546172-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 2421ce10f234..8e26672661dc 100644 --- a/drivers/hid/hid-nintendo.c +++ b/drivers/hid/hid-nintendo.c @@ -328,6 +328,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; @@ -381,6 +382,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); @@ -753,6 +774,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 Tue Mar 17 03:29:27 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Ogorchock X-Patchwork-Id: 208087 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.8 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id 9FE39C2BB1D for ; Tue, 17 Mar 2020 03:30:11 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 7A8AC20717 for ; Tue, 17 Mar 2020 03:30:11 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="A9sKVp4I" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1725783AbgCQDaL (ORCPT ); Mon, 16 Mar 2020 23:30:11 -0400 Received: from mail-qk1-f193.google.com ([209.85.222.193]:37666 "EHLO mail-qk1-f193.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726272AbgCQDaK (ORCPT ); Mon, 16 Mar 2020 23:30:10 -0400 Received: by mail-qk1-f193.google.com with SMTP id z25so25359272qkj.4 for ; Mon, 16 Mar 2020 20:30:10 -0700 (PDT) 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=ixdnztl3QEU2sGssreYnW96/uJjudI7ecVTEuY4Z0jc=; b=A9sKVp4IhOmDXiWPRpW3eR70N95tZU3SamAoUI/2rIHBUfXZqp/XHu5GBPqEfVIczL cAhiUtBOLSPrZx14jRFBRZzipxS9ajSk/mhI2l5jQGQ8Z5zv3+BRx+Mf5BQOu7xL94Et QV5GiW+S9vLbW9w//Y7YuR/9sxTCfbPYQY4A92SqkJ9i+vCLHIvawB9N77YEclmwwf1N 2wB7R2tsD3yWioDBe73zFGyPAXtq6hoSz6B4VNdgsGbEbUwkMSw0D/GaXsnMa5de6nYv ETCNuWvpTo5biFJfOWPUjrfNcyITsENwrUN+xDYVr6fZt/MKjG5cj2BBBf2A6M8nV9J6 iQiA== 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=ixdnztl3QEU2sGssreYnW96/uJjudI7ecVTEuY4Z0jc=; b=ZF+dvGoEQDc7BKjIApjYeTXxUPtZ9qXktmoJygMFkkH0PlK4R/h5yGadKGZKvo2hHA JSf9ra//j+E/CrTfNg59XQFrQmbnKObqRFYcQt5rwot4KKVdpE8hLRItRq/4acRAxLmw 7pczCUjAuKRzbJ44FU0nS3dPX2JpST0PiQkwWeegXzjLkgVzbQALSx9pXcx3wyokA7f1 Sw/rxJK6kOxF9uuGLggN7GAt0p898Z5ANxtbDKePZ8nJ7Fhelpv16jXiJRZnRngI3Ic8 gAkNYfTgjuDPkowbK4DxeywZt7Ak24UDzBCEHGh8raXwS8E+IvlqK3IJSZY7KWVej7Rb ZI/Q== X-Gm-Message-State: ANhLgQ0mxk4Zv1boD1pchS71qVt5EvfWiyWp0Wb0CbO4n1Gr0uXqQTfQ cwSFkXnasgrciBiGLWaRDbWNE5VGxio= X-Google-Smtp-Source: ADFU+vtoMN/v8G8To6CshGwgWbdpDqXh5Lqw6reZCsaLivBu4V3B+XIE7qyukIZppITwQq2I1bm0gA== X-Received: by 2002:a37:514:: with SMTP id 20mr2880317qkf.420.1584415809777; Mon, 16 Mar 2020 20:30:09 -0700 (PDT) Received: from Arrakis.djogorchock.com ([136.33.205.58]) by smtp.gmail.com with ESMTPSA id t2sm1272264qtp.13.2020.03.16.20.30.08 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 16 Mar 2020 20:30:09 -0700 (PDT) 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, carmueller@gmail.com, "Daniel J. Ogorchock" Subject: [PATCH v11 10/11] HID: nintendo: set controller uniq to MAC Date: Mon, 16 Mar 2020 22:29:27 -0500 Message-Id: <20200317032928.546172-11-djogorchock@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200317032928.546172-1-djogorchock@gmail.com> References: <20200317032928.546172-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 a56fa5ffee1a..dbd5946f71e1 100644 --- a/drivers/hid/hid-nintendo.c +++ b/drivers/hid/hid-nintendo.c @@ -319,6 +319,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; @@ -1005,6 +1007,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); @@ -1316,6 +1319,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) @@ -1494,6 +1532,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); /* Initialize the leds */ From patchwork Tue Mar 17 03:29:28 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Ogorchock X-Patchwork-Id: 208086 Return-Path: X-Spam-Checker-Version: SpamAssassin 3.4.0 (2014-02-07) on aws-us-west-2-korg-lkml-1.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-9.8 required=3.0 tests=DKIM_SIGNED,DKIM_VALID, DKIM_VALID_AU,FREEMAIL_FORGED_FROMDOMAIN,FREEMAIL_FROM, HEADER_FROM_DIFFERENT_DOMAINS, INCLUDES_PATCH, MAILING_LIST_MULTI, SIGNED_OFF_BY, SPF_HELO_NONE, SPF_PASS, USER_AGENT_GIT autolearn=ham autolearn_force=no version=3.4.0 Received: from mail.kernel.org (mail.kernel.org [198.145.29.99]) by smtp.lore.kernel.org (Postfix) with ESMTP id BF675C18E5B for ; Tue, 17 Mar 2020 03:30:14 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.kernel.org (Postfix) with ESMTP id 900BC20674 for ; Tue, 17 Mar 2020 03:30:14 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="NMP1VvFZ" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1726250AbgCQDaO (ORCPT ); Mon, 16 Mar 2020 23:30:14 -0400 Received: from mail-qv1-f68.google.com ([209.85.219.68]:40129 "EHLO mail-qv1-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1726278AbgCQDaO (ORCPT ); Mon, 16 Mar 2020 23:30:14 -0400 Received: by mail-qv1-f68.google.com with SMTP id cy12so4245912qvb.7 for ; Mon, 16 Mar 2020 20:30:12 -0700 (PDT) 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=WsCbaQNaqexxH2AzD3qg2AWXRMQJdjg1yUnDdNJB9es=; b=NMP1VvFZeYoJtSQZpjZABRjhjSbALlSlrPk4YJRnj/3V7yw2LKBVpN4jZfa+l7T69b gD2gkWkE+aCgDGuIyvhuyCI+kCRgEq9YOVXElaXd/Tr2uUYqZy744+lnLF1r+P2V584M 3FEttyNnLz9tBxaWA11cLfSwrp7A3vfY21vUg7kGvLL7FjXtQUv5OSkkYHogtfRlg5ca 4psa8bD3QyAxEB8ywJWrxL6I8zIFkvxqyJIk1FveTqcVORI+Ke9GtguX1wJ+EZxo/aAc sX6uNIZH0B5+K5ILg/2l/c0m0/okOegcw4amGQsMkbe3UAWqofYzfYwKgYLWzcdIlje/ 1CdQ== 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=WsCbaQNaqexxH2AzD3qg2AWXRMQJdjg1yUnDdNJB9es=; b=G+FLNGu1U662c4ac/drpj5ALTsL6TmpNIboDUy+8VZM2+zjXCqzy/JUaX0ivWqnx0v F9E692TYKSx1s5yb3KIBlCEDT/rHeGIrQOeDCK8chOKiDCSepUtx4R/Uk9u0Agd03WVS +NiurSMS1f+bVSNJw7L1Y9ca1Fv4v/QjlSaP2X79yGgeyHhUb97ix+cB7CKBo+oXc533 Zj02T6NeogFMxMXbtgtWZsOpjV/poFeZ+ixDUwya2IoT6SeJmhHv6bBSsOQgO6R4sWYo +csjcXVtGgugACyVRIrjmiQ0m6DHwKcE0/9gpeSViyWeAnkHT85Fw42wTnYmfm034vp8 VfxQ== X-Gm-Message-State: ANhLgQ3Qk6cpiFUTKxwKOoJdASwLwv8pfywSo4ZqC22mlbt3kC/+putH AqXc4rpqExUs/Se3G3AUPTUTLc9x8g8= X-Google-Smtp-Source: ADFU+vv5SKMJ12Egj+pK174X9S1k3J01LzTPSxIVXuVB0y5xr1IajDymKWHZtv32WgYSD5hI333ibQ== X-Received: by 2002:a0c:a2c4:: with SMTP id g62mr3039603qva.107.1584415811842; Mon, 16 Mar 2020 20:30:11 -0700 (PDT) Received: from Arrakis.djogorchock.com ([136.33.205.58]) by smtp.gmail.com with ESMTPSA id t2sm1272264qtp.13.2020.03.16.20.30.09 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 16 Mar 2020 20:30:10 -0700 (PDT) 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, carmueller@gmail.com, "Daniel J. Ogorchock" Subject: [PATCH v11 11/11] HID: nintendo: add support for charging grip Date: Mon, 16 Mar 2020 22:29:28 -0500 Message-Id: <20200317032928.546172-12-djogorchock@gmail.com> X-Mailer: git-send-email 2.25.1 In-Reply-To: <20200317032928.546172-1-djogorchock@gmail.com> References: <20200317032928.546172-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 the joy-con charging grip. The peripheral essentially behaves the same as a pro controller, but with two joy-cons attached to the grip. However the grip exposes the two joy-cons as separate hid devices, so extra handling is required. The joy-con is queried to check if it is a right or left joy-con (since the product ID is identical between left/right when using the grip). Since controller model detection is now more complicated, the various checks for hid product values have been replaced with helper macros to reduce code duplication. Signed-off-by: Daniel J. Ogorchock --- drivers/hid/hid-ids.h | 1 + drivers/hid/hid-nintendo.c | 67 ++++++++++++++++++++++++++++++-------- 2 files changed, 55 insertions(+), 13 deletions(-) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 452b4228a196..e50f3f32d94e 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -878,6 +878,7 @@ #define USB_DEVICE_ID_NINTENDO_JOYCONL 0x2006 #define USB_DEVICE_ID_NINTENDO_JOYCONR 0x2007 #define USB_DEVICE_ID_NINTENDO_PROCON 0x2009 +#define USB_DEVICE_ID_NINTENDO_CHRGGRIP 0x200E #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 index dbd5946f71e1..69505193b367 100644 --- a/drivers/hid/hid-nintendo.c +++ b/drivers/hid/hid-nintendo.c @@ -233,6 +233,13 @@ enum joycon_ctlr_state { JOYCON_CTLR_STATE_REMOVED, }; +/* Controller type received as part of device info */ +enum joycon_ctlr_type { + JOYCON_CTLR_TYPE_JCL = 0x01, + JOYCON_CTLR_TYPE_JCR = 0x02, + JOYCON_CTLR_TYPE_PRO = 0x03, +}; + struct joycon_stick_cal { s32 max; s32 min; @@ -321,6 +328,7 @@ struct joycon_ctlr { spinlock_t lock; u8 mac_addr[6]; char *mac_addr_str; + enum joycon_ctlr_type ctlr_type; /* The following members are used for synchronous sends/receives */ enum joycon_msg_type msg_type; @@ -359,6 +367,26 @@ struct joycon_ctlr { u16 rumble_rh_freq; }; +/* Helper macros for checking controller type */ +#define jc_type_is_joycon(ctlr) \ + (ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_JOYCONL || \ + ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_JOYCONR || \ + ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_CHRGGRIP) +#define jc_type_is_procon(ctlr) \ + (ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_PROCON) +#define jc_type_is_chrggrip(ctlr) \ + (ctlr->hdev->product == USB_DEVICE_ID_NINTENDO_CHRGGRIP) + +/* Does this controller have inputs associated with left joycon? */ +#define jc_type_has_left(ctlr) \ + (ctlr->ctlr_type == JOYCON_CTLR_TYPE_JCL || \ + ctlr->ctlr_type == JOYCON_CTLR_TYPE_PRO) + +/* Does this controller have inputs associated with right joycon? */ +#define jc_type_has_right(ctlr) \ + (ctlr->ctlr_type == JOYCON_CTLR_TYPE_JCR || \ + ctlr->ctlr_type == JOYCON_CTLR_TYPE_PRO) + static int __joycon_hid_send(struct hid_device *hdev, u8 *data, size_t len) { u8 *buf; @@ -655,7 +683,6 @@ static void joycon_parse_report(struct joycon_ctlr *ctlr, unsigned long flags; u8 tmp; u32 btns; - u32 id = ctlr->hdev->product; unsigned long msecs = jiffies_to_msecs(jiffies); spin_lock_irqsave(&ctlr->lock, flags); @@ -694,7 +721,7 @@ static void joycon_parse_report(struct joycon_ctlr *ctlr, /* Parse the buttons and sticks */ btns = hid_field_extract(ctlr->hdev, rep->button_status, 0, 24); - if (id != USB_DEVICE_ID_NINTENDO_JOYCONR) { + if (jc_type_has_left(ctlr)) { u16 raw_x; u16 raw_y; s32 x; @@ -718,7 +745,7 @@ static void joycon_parse_report(struct joycon_ctlr *ctlr, input_report_key(dev, BTN_THUMBL, btns & JC_BTN_LSTICK); input_report_key(dev, BTN_Z, btns & JC_BTN_CAP); - if (id != USB_DEVICE_ID_NINTENDO_PROCON) { + if (jc_type_is_joycon(ctlr)) { /* 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); @@ -750,7 +777,7 @@ static void joycon_parse_report(struct joycon_ctlr *ctlr, input_report_abs(dev, ABS_HAT0Y, haty); } } - if (id != USB_DEVICE_ID_NINTENDO_JOYCONL) { + if (jc_type_has_right(ctlr)) { u16 raw_x; u16 raw_y; s32 x; @@ -770,7 +797,7 @@ static void joycon_parse_report(struct joycon_ctlr *ctlr, /* 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) { + if (jc_type_is_joycon(ctlr)) { /* 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); @@ -989,6 +1016,12 @@ static int joycon_input_create(struct joycon_ctlr *ctlr) case USB_DEVICE_ID_NINTENDO_PROCON: name = "Nintendo Switch Pro Controller"; break; + case USB_DEVICE_ID_NINTENDO_CHRGGRIP: + if (jc_type_has_left(ctlr)) + name = "Nintendo Switch Left Joy-Con (Grip)"; + else + name = "Nintendo Switch Right Joy-Con (Grip)"; + break; case USB_DEVICE_ID_NINTENDO_JOYCONL: name = "Nintendo Switch Left Joy-Con"; break; @@ -1011,9 +1044,8 @@ static int joycon_input_create(struct joycon_ctlr *ctlr) ctlr->input->name = name; input_set_drvdata(ctlr->input, ctlr); - /* set up sticks and buttons */ - if (hdev->product != USB_DEVICE_ID_NINTENDO_JOYCONR) { + if (jc_type_has_left(ctlr)) { input_set_abs_params(ctlr->input, ABS_X, -JC_MAX_STICK_MAG, JC_MAX_STICK_MAG, JC_STICK_FUZZ, JC_STICK_FLAT); @@ -1039,7 +1071,7 @@ static int joycon_input_create(struct joycon_ctlr *ctlr) JC_DPAD_FUZZ, JC_DPAD_FLAT); } } - if (hdev->product != USB_DEVICE_ID_NINTENDO_JOYCONL) { + if (jc_type_has_right(ctlr)) { input_set_abs_params(ctlr->input, ABS_RX, -JC_MAX_STICK_MAG, JC_MAX_STICK_MAG, JC_STICK_FUZZ, JC_STICK_FLAT); @@ -1207,7 +1239,7 @@ static int joycon_leds_create(struct joycon_ctlr *ctlr) mutex_unlock(&joycon_input_num_mutex); /* configure the home LED */ - if (ctlr->hdev->product != USB_DEVICE_ID_NINTENDO_JOYCONL) { + if (jc_type_has_right(ctlr)) { name = devm_kasprintf(dev, GFP_KERNEL, "%s:%s", d_name, "home"); if (!name) return ret; @@ -1319,7 +1351,7 @@ static int joycon_power_supply_create(struct joycon_ctlr *ctlr) return 0; } -static int joycon_read_mac(struct joycon_ctlr *ctlr) +static int joycon_read_info(struct joycon_ctlr *ctlr) { int ret; int i; @@ -1351,6 +1383,9 @@ static int joycon_read_mac(struct joycon_ctlr *ctlr) return -ENOMEM; hid_info(ctlr->hdev, "controller MAC = %s\n", ctlr->mac_addr_str); + /* Retrieve the type so we can distinguish for charging grip */ + ctlr->ctlr_type = report->reply.data[2]; + return 0; } @@ -1486,7 +1521,7 @@ static int nintendo_hid_probe(struct hid_device *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 && + if ((jc_type_is_procon(ctlr) || jc_type_is_chrggrip(ctlr)) && !joycon_send_usb(ctlr, JC_USB_CMD_HANDSHAKE, HZ)) { hid_dbg(hdev, "detected USB controller\n"); /* set baudrate for improved latency */ @@ -1506,6 +1541,10 @@ static int nintendo_hid_probe(struct hid_device *hdev, * This doesn't send a response, so ignore the timeout. */ joycon_send_usb(ctlr, JC_USB_CMD_NO_TIMEOUT, HZ/10); + } else if (jc_type_is_chrggrip(ctlr)) { + hid_err(hdev, "Failed charging grip handshake\n"); + ret = -ETIMEDOUT; + goto err_mutex; } /* get controller calibration data, and parse it */ @@ -1532,9 +1571,9 @@ static int nintendo_hid_probe(struct hid_device *hdev, goto err_mutex; } - ret = joycon_read_mac(ctlr); + ret = joycon_read_info(ctlr); if (ret) { - hid_err(hdev, "Failed to retrieve controller MAC; ret=%d\n", + hid_err(hdev, "Failed to retrieve controller info; ret=%d\n", ret); goto err_close; } @@ -1602,6 +1641,8 @@ static const struct hid_device_id nintendo_hid_devices[] = { USB_DEVICE_ID_NINTENDO_PROCON) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_PROCON) }, + { HID_USB_DEVICE(USB_VENDOR_ID_NINTENDO, + USB_DEVICE_ID_NINTENDO_CHRGGRIP) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_JOYCONL) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO,