From patchwork Mon Mar 11 02:03:25 2019 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Daniel Ogorchock X-Patchwork-Id: 10846571 X-Patchwork-Delegate: jikos@jikos.cz Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork-2.web.codeaurora.org (Postfix) with ESMTP id 2B6521515 for ; Mon, 11 Mar 2019 02:04:19 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 17A8C28D49 for ; Mon, 11 Mar 2019 02:04:19 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id 0BC9828D52; Mon, 11 Mar 2019 02:04:19 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-8.0 required=2.0 tests=BAYES_00,DKIM_SIGNED, DKIM_VALID,DKIM_VALID_AU,FREEMAIL_FROM,MAILING_LIST_MULTI,RCVD_IN_DNSWL_HI autolearn=ham version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 6672328D49 for ; Mon, 11 Mar 2019 02:04:18 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1727151AbfCKCER (ORCPT ); Sun, 10 Mar 2019 22:04:17 -0400 Received: from mail-yw1-f68.google.com ([209.85.161.68]:41537 "EHLO mail-yw1-f68.google.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1727156AbfCKCER (ORCPT ); Sun, 10 Mar 2019 22:04:17 -0400 Received: by mail-yw1-f68.google.com with SMTP id q128so2620248ywg.8 for ; Sun, 10 Mar 2019 19:04:16 -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=sf/fnyTSc33lvp768LHLlF9PFjGVDRaQt2XB67nnkS8=; b=Cxf7IlgEQbM7EBYIrXrid1c8qbQjbbXLGnjvVI1qKi0kcLrL1qDh3wtRmMYq82Y2KE mYfDLfOAaJADrua4imkXV7sYd5GqPPFzDHUmOoeR5rHgrzK0P/50+pPTWRhMiIsR2lTU 4sL7JfmzWzFGZqkCAcLPPlRZDCI3te4rs7Vb0BvLtzpjkaeKG2qza0j1Ptl7ykgFWs6q XgiVbjCDlXUKbYOS9bakW+omAbwa8dn6hbYn6llGlZwrXwSoVGRRH9UlCg952+LiOpMt oSnCvGnJBhXbQwiXNmxVeBIpqLXOIk82rLdxEBEOAhcPuvuZmvuk1JozlLfuwEsN+Riu /9hQ== 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=sf/fnyTSc33lvp768LHLlF9PFjGVDRaQt2XB67nnkS8=; b=qTSv9zr5CLoDbSp8LxO5paCz3vwJT+8dfuQYB4LT/3DqUV8CGZiRj0bVzsJnfd96q4 EwydWn2lCV3JuNTcbCX0b90Fby8h3eYjpSJo54sD+MD3P86zGW4M1Q2xvZENeIZbEiIV O/iUr0Ydhzq4mhZKcR652IO2ndssnZ50wUBdWyd+plYb22vKqtVwAP/1orAJ8H3hT2cf HUoF86y9SsM0PgCZegt59GocKO0ex+I9kQEknmcSHWjqb3y2IUcZV5zyYmze31UlHpRU 2yrloY3UqR+Ik+dGjZd1NBer+MHzCOBmWVkPbagF+CeN6n98ntsPnAUAdNS+M7lxUHSo CbZA== X-Gm-Message-State: APjAAAVl6pkJRD9YdGs8ArgIqhgRQvFaEP4nheob5Ql+/XQjCBZlwNM7 ui9hJ4nBy0mtdhPRC876kDeZ6Gr3WSA= X-Google-Smtp-Source: APXvYqxTPjVr045jHTTM6YAYR95IkBWhiR9aPtx7XJiHYZ8Tg/vQVZE1vaCh3CXi/WlUJf/0hajppQ== X-Received: by 2002:a25:23d8:: with SMTP id j207mr25101127ybj.0.1552269856201; Sun, 10 Mar 2019 19:04:16 -0700 (PDT) Received: from localhost.localdomain ([136.61.179.39]) by smtp.gmail.com with ESMTPSA id r130sm1907088ywr.4.2019.03.10.19.04.15 (version=TLS1_2 cipher=ECDHE-RSA-AES128-GCM-SHA256 bits=128/128); Sun, 10 Mar 2019 19:04:15 -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, peter.hutterer@who-t.net, jbrandst@2ds.eu, hadess@hadess.net, "Daniel J. Ogorchock" Subject: [PATCH v4 6/6] HID: joycon: add rumble frequency sysfs attributes Date: Sun, 10 Mar 2019 21:03:25 -0500 Message-Id: <20190311020324.23619-7-djogorchock@gmail.com> X-Mailer: git-send-email 2.21.0 In-Reply-To: <20190311020324.23619-1-djogorchock@gmail.com> References: <20190311020324.23619-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 X-Virus-Scanned: ClamAV using ClamSMTP This patch adds four sysfs attributes (one for each configurable rumble frequency). This allows userspace software to play different tones on the joy-cons by varying the frequency. Signed-off-by: Daniel J. Ogorchock --- drivers/hid/hid-joycon.c | 161 ++++++++++++++++++++++++++++++++++++--- 1 file changed, 151 insertions(+), 10 deletions(-) diff --git a/drivers/hid/hid-joycon.c b/drivers/hid/hid-joycon.c index 10460bac3307..a8ea8b09265d 100644 --- a/drivers/hid/hid-joycon.c +++ b/drivers/hid/hid-joycon.c @@ -180,8 +180,6 @@ static const struct joycon_rumble_freq_data joycon_rumble_frequencies[] = { { 0xe801, 0x00, 1124 }, { 0xec01, 0x00, 1149 }, { 0xf001, 0x00, 1174 }, { 0xf401, 0x00, 1199 }, { 0xf801, 0x00, 1226 }, { 0xfc01, 0x00, 1253 } }; -static const u16 joycon_max_rumble_freq = -joycon_rumble_frequencies[ARRAY_SIZE(joycon_rumble_frequencies) - 1].freq; static const struct joycon_rumble_amp_data joycon_rumble_amplitudes[] = { /* high, low, amp */ @@ -353,6 +351,8 @@ struct joycon_ctlr { u16 rumble_lh_freq; u16 rumble_rl_freq; u16 rumble_rh_freq; + u16 rumble_l_amp; + u16 rumble_r_amp; }; static int __joycon_hid_send(struct hid_device *hdev, u8 *data, size_t len) @@ -762,11 +762,34 @@ static void joycon_encode_rumble(u8 *data, u16 freq_low, u16 freq_high, u16 amp) data[3] = amp_data.low & 0xFF; } +#define JOYCON_MAX_RUMBLE_HIGH_FREQ ((u16) 1253) +#define JOYCON_MIN_RUMBLE_HIGH_FREQ ((u16) 82) +#define JOYCON_MAX_RUMBLE_LOW_FREQ ((u16) 626) +#define JOYCON_MIN_RUMBLE_LOW_FREQ ((u16) 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) { u8 data[8]; - u16 freq_low; - u16 freq_high; u16 amp; u16 freq_r_low; u16 freq_r_high; @@ -782,16 +805,14 @@ static int joycon_set_rumble(struct joycon_ctlr *ctlr, u16 amp_r, u16 amp_l) spin_unlock_irqrestore(&ctlr->lock, flags); /* right joy-con */ - freq_low = min(freq_r_low, joycon_max_rumble_freq); - freq_high = min(freq_r_high, joycon_max_rumble_freq); amp = amp_r * (u32)joycon_max_rumble_amp / 65535; - joycon_encode_rumble(data + 4, freq_low, freq_high, amp); + ctlr->rumble_r_amp = amp; + joycon_encode_rumble(data + 4, freq_r_low, freq_r_high, amp); /* left joy-con */ - freq_low = min(freq_l_low, joycon_max_rumble_freq); - freq_high = min(freq_l_high, joycon_max_rumble_freq); amp = amp_l * (u32)joycon_max_rumble_amp / 65535; - joycon_encode_rumble(data, freq_low, freq_high, amp); + ctlr->rumble_l_amp = amp; + joycon_encode_rumble(data, freq_l_low, freq_l_high, amp); spin_lock_irqsave(&ctlr->lock, flags); memcpy(ctlr->rumble_data, data, sizeof(ctlr->rumble_data)); @@ -1241,6 +1262,111 @@ static void joycon_ctlr_destroy(struct joycon_ctlr *ctlr) mutex_destroy(&ctlr->output_mutex); } +static ssize_t joycon_show_freq(struct device *dev, + struct device_attribute *attr, char *buf); + +static ssize_t joycon_store_freq(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count); + +static DEVICE_ATTR(left_high_freq, 0644, + joycon_show_freq, joycon_store_freq); +static DEVICE_ATTR(left_low_freq, 0644, + joycon_show_freq, joycon_store_freq); +static DEVICE_ATTR(right_high_freq, 0644, + joycon_show_freq, joycon_store_freq); +static DEVICE_ATTR(right_low_freq, 0644, + joycon_show_freq, joycon_store_freq); + +static ssize_t joycon_show_freq(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct hid_device *hdev = to_hid_device(dev); + struct joycon_ctlr *ctlr = hid_get_drvdata(hdev); + const char *name = attr->attr.name; + u16 val; + + if (!strcmp(name, dev_attr_left_high_freq.attr.name)) + val = ctlr->rumble_lh_freq; + else if (!strcmp(name, dev_attr_left_low_freq.attr.name)) + val = ctlr->rumble_ll_freq; + else if (!strcmp(name, dev_attr_right_high_freq.attr.name)) + val = ctlr->rumble_rh_freq; + else + val = ctlr->rumble_rl_freq; + + return scnprintf(buf, PAGE_SIZE, "%hu\n", val); +} + +static ssize_t joycon_store_freq(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct hid_device *hdev = to_hid_device(dev); + struct joycon_ctlr *ctlr = hid_get_drvdata(hdev); + const char *name = attr->attr.name; + int ret; + u16 *val; + + if (!strcmp(name, dev_attr_left_high_freq.attr.name)) + val = &ctlr->rumble_lh_freq; + else if (!strcmp(name, dev_attr_left_low_freq.attr.name)) + val = &ctlr->rumble_ll_freq; + else if (!strcmp(name, dev_attr_right_high_freq.attr.name)) + val = &ctlr->rumble_rh_freq; + else + val = &ctlr->rumble_rl_freq; + + ret = kstrtou16(buf, 10, val); + if (ret >= 0) + ret = count; + + joycon_clamp_rumble_freqs(ctlr); + joycon_set_rumble(ctlr, ctlr->rumble_r_amp, ctlr->rumble_l_amp); + + return ret; +} + +static int joycon_create_sysfs_attribs(struct joycon_ctlr *ctlr) +{ + struct device *dev = &ctlr->hdev->dev; + int ret = 0; + + if (ctlr->hdev->product != USB_DEVICE_ID_NINTENDO_JOYCONL) { + ret = device_create_file(dev, &dev_attr_right_high_freq); + if (!ret) { + ret = device_create_file(dev, &dev_attr_right_low_freq); + if (ret) + device_remove_file(dev, + &dev_attr_right_high_freq); + } + } + if (ctlr->hdev->product != USB_DEVICE_ID_NINTENDO_JOYCONR && !ret) { + ret = device_create_file(dev, &dev_attr_left_high_freq); + if (!ret) { + ret = device_create_file(dev, &dev_attr_left_low_freq); + if (ret) + device_remove_file(dev, + &dev_attr_left_high_freq); + } + } + return ret; +} + +static void joycon_remove_sysfs_attribs(struct joycon_ctlr *ctlr) +{ + struct device *dev = &ctlr->hdev->dev; + + if (ctlr->hdev->product != USB_DEVICE_ID_NINTENDO_JOYCONL) { + device_remove_file(dev, &dev_attr_right_high_freq); + device_remove_file(dev, &dev_attr_right_low_freq); + } + if (ctlr->hdev->product != USB_DEVICE_ID_NINTENDO_JOYCONR) { + device_remove_file(dev, &dev_attr_left_high_freq); + device_remove_file(dev, &dev_attr_left_low_freq); + } +} + static int joycon_hid_probe(struct hid_device *hdev, const struct hid_device_id *id) { @@ -1330,6 +1456,16 @@ static int joycon_hid_probe(struct hid_device *hdev, goto err_close; } + /* Create the sysfs entries for configuring rumble frequencies */ + if (IS_ENABLED(CONFIG_JOYCON_FF)) { + ret = joycon_create_sysfs_attribs(ctlr); + if (ret) { + hid_err(hdev, "Failed to create sysfs attrs; ret=%d\n", + ret); + goto err_close; + } + } + /* Initialize the leds */ ret = joycon_leds_create(ctlr); if (ret) { @@ -1368,6 +1504,11 @@ static void joycon_hid_remove(struct hid_device *hdev) hid_dbg(hdev, "remove\n"); hid_hw_close(hdev); hid_hw_stop(hdev); + + /* Remove the sysfs entries if rumble is enabled */ + if (IS_ENABLED(CONFIG_JOYCON_FF)) + joycon_remove_sysfs_attribs(ctlr); + joycon_ctlr_destroy(ctlr); }