From patchwork Wed May 6 00:47:59 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Fran=C3=A7ois-Xavier_Carton?= X-Patchwork-Id: 11530107 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 11B2615AB for ; Wed, 6 May 2020 00:46:25 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id E47FA20735 for ; Wed, 6 May 2020 00:46:24 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="BDbHz3EL" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729505AbgEFAqY (ORCPT ); Tue, 5 May 2020 20:46:24 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34974 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729657AbgEFAqX (ORCPT ); Tue, 5 May 2020 20:46:23 -0400 Received: from mail-qt1-x842.google.com (mail-qt1-x842.google.com [IPv6:2607:f8b0:4864:20::842]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 737FEC061A10 for ; Tue, 5 May 2020 17:46:23 -0700 (PDT) Received: by mail-qt1-x842.google.com with SMTP id w29so63827qtv.3 for ; Tue, 05 May 2020 17:46:23 -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:mime-version :content-transfer-encoding; bh=RafLOJAWdfSmx4o5+Z6avHMWrrs6wpIzm2BZi9i95Ig=; b=BDbHz3ELiID5qizQH06VVZWEZaV9hLcr41+Y/5gyS7o+yxbC8d9DFpJWUs+MQDqc8d qtla+7AN4lSQ7oYjjwJWeyiVY4ttWQJE2wSOA9AicAWKBAO2pLtChs5i67CjLbNfpLfO eEfVxKLJo5JerR3o9XjEFiQ/X3rvSdedDHuXzgVMIwsr7O1xdf9XM8kl8/a7KBr7QIa8 J4BINw8Axg2TTRg0Wf85T7Jt/p79XxYcmLgtGh26nvRmBKSr8HTPjblpJdmnGepbj2Hy oip6MGor7F3dEpGlBL0mdf7AI5FxwxIdwCPTORoPhrtxF9HFiDyrqY6gm57DKHpyrzjv TYvA== 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:mime-version :content-transfer-encoding; bh=RafLOJAWdfSmx4o5+Z6avHMWrrs6wpIzm2BZi9i95Ig=; b=J83lgbzE+GbHtxZQ7fL9tIWyc7Z4e3CqVUQF5FlbMF5Riy8cu1dlq68ej8E1OZ+pwh lp5iS2XpqZLDCDj69PdeNWlOx6s2j4snb0AYR2opvRou/HArZqQEyJuqPLJ6Pao5bFgp 0952bP11DhO7opa+x49KzEZfoSdvaZ+4cF5wbt8yZUjJGzt5+abPvq+vzM7i3PaZKqjx n38f+vZwlYnvlAVHofvx5CpqLeS0ihKNHHgBYgz2EcZDCqHd4nGoz/dOpgRc0nQPJy6l 75qStgxcojed5azaujJJ7ACPCpB3QOL8QuXX8crhtQaBz4fWbdJI+C7cyF1AcLsYO7B6 gHiA== X-Gm-Message-State: AGi0Pub7t/fNPUP/pyfz4etrLWHP4114/K3lfp4EDDTvWJ9P5OwisGuu lCvlBGdZwpHJwKuUAGHIJfIdrzrx X-Google-Smtp-Source: APiQypLtEJfGLypaDrXNTFvcrFmFudZkrCFIpowrntXQ6aNxYvkBmAbKIKfEA2EbYvwZuIPOoCpUrg== X-Received: by 2002:ac8:36e9:: with SMTP id b38mr5754503qtc.234.1588725982305; Tue, 05 May 2020 17:46:22 -0700 (PDT) Received: from localhost ([2601:483:4200:9113:fdae:121b:56a3:4870]) by smtp.gmail.com with ESMTPSA id c124sm487445qke.13.2020.05.05.17.46.21 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 05 May 2020 17:46:21 -0700 (PDT) From: =?utf-8?q?Fran=C3=A7ois-Xavier_Carton?= To: linux-input@vger.kernel.org Cc: =?utf-8?q?Fran=C3=A7ois-Xavier_Carton?= Subject: [PATCH 1/3] HID: gamecube-adapter: add nintendo gamecube adapter Date: Wed, 6 May 2020 02:47:59 +0200 Message-Id: <20200506004801.9478-1-fx.carton91@gmail.com> X-Mailer: git-send-email 2.26.2 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-gamecube-adapter driver supports Nintendo Gamecube Controller Adapters. They are USB devices on which up to four Nintendo Gamecube Controllers can be plugged. The driver create independent input devices as controllers are connected. Signed-off-by: François-Xavier Carton --- MAINTAINERS | 6 + drivers/hid/Kconfig | 10 + drivers/hid/Makefile | 1 + drivers/hid/hid-gamecube-adapter.c | 386 +++++++++++++++++++++++++++++ drivers/hid/hid-ids.h | 1 + 5 files changed, 404 insertions(+) create mode 100644 drivers/hid/hid-gamecube-adapter.c diff --git a/MAINTAINERS b/MAINTAINERS index d5b1878f2815..585ddcf3a6dd 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -7025,6 +7025,12 @@ F: scripts/gcc-plugin.sh F: scripts/Makefile.gcc-plugins F: Documentation/kbuild/gcc-plugins.rst +GAMECUBE ADAPTER HID DRIVER +M: François-Xavier Carton +L: linux-input@vger.kernel.org +S: Maintained +F: drivers/hid/hid-gamecube-adapter* + GASKET DRIVER FRAMEWORK M: Rob Springer M: Todd Poynor diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index 7c89edbd6c5a..d49e261a74f6 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -350,6 +350,16 @@ config HID_EZKEY ---help--- Support for Ezkey BTC 8193 keyboard. +config HID_GAMECUBE_ADAPTER + tristate "Nintendo Gamecube Controller Adapter support" + depends on HID + depends on USB_HID + ---help--- + Support for the Nintendo Gamecube Controller Adapter. + + To compile this driver as a module, choose M here: the + module will be called hid-gamecube-adapter. + config HID_GEMBIRD tristate "Gembird Joypad" depends on HID diff --git a/drivers/hid/Makefile b/drivers/hid/Makefile index d8ea4b8c95af..9cddc4d48db8 100644 --- a/drivers/hid/Makefile +++ b/drivers/hid/Makefile @@ -46,6 +46,7 @@ obj-$(CONFIG_HID_ELAN) += hid-elan.o obj-$(CONFIG_HID_ELECOM) += hid-elecom.o obj-$(CONFIG_HID_ELO) += hid-elo.o obj-$(CONFIG_HID_EZKEY) += hid-ezkey.o +obj-$(CONFIG_HID_GAMECUBE_ADAPTER) += hid-gamecube-adapter.o obj-$(CONFIG_HID_GEMBIRD) += hid-gembird.o obj-$(CONFIG_HID_GFRM) += hid-gfrm.o obj-$(CONFIG_HID_GLORIOUS) += hid-glorious.o diff --git a/drivers/hid/hid-gamecube-adapter.c b/drivers/hid/hid-gamecube-adapter.c new file mode 100644 index 000000000000..0537ece0979a --- /dev/null +++ b/drivers/hid/hid-gamecube-adapter.c @@ -0,0 +1,386 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * HID driver for Nintendo Gamecube Controller Adapters + * + * Copyright (c) 2020 François-Xavier Carton + * + * This driver is based on: + * https://github.com/ToadKing/wii-u-gc-adapter + * drivers/hid/hid-wiimote-core.c + * drivers/hid/hid-steam.c + * + */ + +#include "hid-ids.h" +#include +#include +#include +#include +#include +#include "usbhid/usbhid.h" + +enum gamecube_output { + GC_CMD_INIT = 0x13 +}; + +enum gamecube_input { + GC_INPUT_REPORT = 0x21 +}; + +#define GC_INPUT_REPORT_SIZE 37 + +enum gamecube_ctrl_flags { + GC_FLAG_EXTRAPOWER = BIT(2), + GC_TYPE_NORMAL = BIT(4), + GC_TYPE_WAVEBIRD = BIT(5), + GC_TYPES = GC_TYPE_NORMAL | GC_TYPE_WAVEBIRD +}; + +enum gamecube_btn { + GC_BTN_START = BIT(0), + GC_BTN_Z = BIT(1), + GC_BTN_R = BIT(2), + GC_BTN_L = BIT(3), + GC_BTN_A = BIT(8), + GC_BTN_B = BIT(9), + GC_BTN_X = BIT(10), + GC_BTN_Y = BIT(11), + GC_BTN_DPAD_LEFT = BIT(12), + GC_BTN_DPAD_RIGHT = BIT(13), + GC_BTN_DPAD_DOWN = BIT(14), + GC_BTN_DPAD_UP = BIT(15), +}; + +struct gamecube_ctrl { + struct input_dev __rcu *input; + enum gamecube_ctrl_flags flags; + struct gamecube_adapter *adpt; + struct work_struct work_connect; + spinlock_t flags_lock; +}; + +struct gamecube_adapter { + struct gamecube_ctrl ctrls[4]; + struct hid_device *hdev; +}; + +static int gamecube_hid_send(struct hid_device *hdev, const u8 *data, size_t n) +{ + u8 *buf; + int ret; + + buf = kmemdup(data, n, GFP_KERNEL); + if (!buf) + return -ENOMEM; + ret = hid_hw_output_report(hdev, buf, n); + kfree(buf); + return ret; +} + +static int gamecube_send_cmd_init(struct hid_device *hdev) +{ + const u8 initcmd[] = {GC_CMD_INIT}; + return gamecube_hid_send(hdev, initcmd, sizeof(initcmd)); +} + +static const unsigned int gamecube_buttons[] = { + BTN_START, BTN_TR2, BTN_TR, BTN_TL, + BTN_SOUTH, BTN_WEST, BTN_EAST, BTN_NORTH, + BTN_DPAD_LEFT, BTN_DPAD_RIGHT, BTN_DPAD_DOWN, BTN_DPAD_UP +}; + +static const unsigned int gamecube_axes[] = { + ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_Z, ABS_RZ +}; + +static const char* gamecube_ctrl_name(enum gamecube_ctrl_flags flags) +{ + switch (flags & GC_TYPES) { + case GC_TYPE_NORMAL: + return "Standard Gamecube Controller"; + case GC_TYPE_WAVEBIRD: + return "Wavebird Gamecube Controller"; + } + return NULL; +} + +static int gamecube_ctrl_create(struct gamecube_ctrl *ctrl) +{ + struct input_dev *input; + struct hid_device *hdev = ctrl->adpt->hdev; + const char *name; + unsigned int i; + int ret; + + name = gamecube_ctrl_name(ctrl->flags); + if (!name) { + unsigned int num = ctrl - ctrl->adpt->ctrls; + hid_warn(hdev, "port %u: unknown controller plugged in\n", num + 1); + return -EINVAL; + } + + input = input_allocate_device(); + if (!input) + return -ENOMEM; + + input_set_drvdata(input, ctrl); + input->id.bustype = hdev->bus; + input->id.vendor = hdev->vendor; + input->id.product = hdev->product; + input->id.version = hdev->version; + input->name = name; + + for (i = 0; i < ARRAY_SIZE(gamecube_buttons); i++) + input_set_capability(input, EV_KEY, gamecube_buttons[i]); + for (i = 0; i < ARRAY_SIZE(gamecube_axes); i++) + input_set_abs_params(input, gamecube_axes[i], 0, 255, 0, 0); + + ret = input_register_device(input); + if (ret) + goto err_free_device; + + rcu_assign_pointer(ctrl->input, input); + return 0; + +err_free_device: + input_free_device(input); + return ret; +} + +static void gamecube_ctrl_destroy(struct gamecube_ctrl *ctrl) +{ + struct input_dev *input; + rcu_read_lock(); + input = rcu_dereference(ctrl->input); + rcu_read_unlock(); + if (!input) + return; + RCU_INIT_POINTER(ctrl->input, NULL); + synchronize_rcu(); + input_unregister_device(input); +} + +static void gamecube_work_connect_cb(struct work_struct *work) +{ + struct gamecube_ctrl *ctrl = container_of(work, struct gamecube_ctrl, work_connect); + struct input_dev *input; + unsigned long irq_flags; + unsigned int num = ctrl - ctrl->adpt->ctrls; + u8 type; + + spin_lock_irqsave(&ctrl->flags_lock, irq_flags); + type = ctrl->flags & GC_TYPES; + spin_unlock_irqrestore(&ctrl->flags_lock, irq_flags); + + rcu_read_lock(); + input = rcu_dereference(ctrl->input); + rcu_read_unlock(); + + if (type && input) { + hid_info(ctrl->adpt->hdev, "port %u: already connected\n", num + 1); + } else if (type) { + hid_info(ctrl->adpt->hdev, "port %u: controller plugged in\n", num + 1); + gamecube_ctrl_create(ctrl); + } else if (input) { + hid_info(ctrl->adpt->hdev, "port %u: controller unplugged\n", num + 1); + gamecube_ctrl_destroy(ctrl); + } +} + +static void gamecube_ctrl_handle_report(struct gamecube_ctrl *ctrl, u8 *data) +{ + struct input_dev *dev; + u16 btns = data[1] << 8 | data[2]; + u8 old_flags, new_flags = data[0]; + unsigned long irq_flags; + + spin_lock_irqsave(&ctrl->flags_lock, irq_flags); + old_flags = ctrl->flags; + ctrl->flags = new_flags; + spin_unlock_irqrestore(&ctrl->flags_lock, irq_flags); + + if ((new_flags & GC_TYPES) != (old_flags & GC_TYPES)) { + schedule_work(&ctrl->work_connect); + return; + } + if (!(new_flags & GC_TYPES)) + return; + + rcu_read_lock(); + dev = rcu_dereference(ctrl->input); + if (!dev) + goto unlock; + + input_report_key(dev, BTN_START, btns & GC_BTN_START); + input_report_key(dev, BTN_TR2, btns & GC_BTN_Z); + input_report_key(dev, BTN_TR, btns & GC_BTN_R); + input_report_key(dev, BTN_TL, btns & GC_BTN_L); + input_report_key(dev, BTN_SOUTH, btns & GC_BTN_A); + input_report_key(dev, BTN_WEST, btns & GC_BTN_B); + input_report_key(dev, BTN_EAST, btns & GC_BTN_X); + input_report_key(dev, BTN_NORTH, btns & GC_BTN_Y); + input_report_key(dev, BTN_DPAD_LEFT, btns & GC_BTN_DPAD_LEFT); + input_report_key(dev, BTN_DPAD_RIGHT, btns & GC_BTN_DPAD_RIGHT); + input_report_key(dev, BTN_DPAD_DOWN, btns & GC_BTN_DPAD_DOWN); + input_report_key(dev, BTN_DPAD_UP, btns & GC_BTN_DPAD_UP); + input_report_abs(dev, ABS_X, data[3]); + input_report_abs(dev, ABS_Y, 255 - data[4]); + input_report_abs(dev, ABS_RX, data[5]); + input_report_abs(dev, ABS_RY, 255 - data[6]); + input_report_abs(dev, ABS_Z, data[7]); + input_report_abs(dev, ABS_RZ, data[8]); + input_sync(dev); + +unlock: + rcu_read_unlock(); +} + +static int gamecube_hid_event(struct hid_device *hdev, + struct hid_report *report, u8 *raw_data, int size) +{ + struct gamecube_adapter *adpt = hid_get_drvdata(hdev); + unsigned int i; + + if (size < 1) + return -EINVAL; + if (size == GC_INPUT_REPORT_SIZE && raw_data[0] == GC_INPUT_REPORT) { + for (i = 0; i < 4; i++) + gamecube_ctrl_handle_report(adpt->ctrls + i, raw_data + 1 + 9 * i); + } else { + hid_warn(hdev, "unhandled event\n"); + } + + return 0; +} + +static struct gamecube_adapter* gamecube_adpt_create(struct hid_device *hdev) +{ + struct gamecube_adapter *adpt; + unsigned int i; + + adpt = kzalloc(sizeof(*adpt), GFP_KERNEL); + if (!adpt) + return NULL; + + adpt->hdev = hdev; + hid_set_drvdata(hdev, adpt); + + for (i = 0; i < 4; i++) { + adpt->ctrls[i].adpt = adpt; + INIT_WORK(&adpt->ctrls[i].work_connect, gamecube_work_connect_cb); + spin_lock_init(&adpt->ctrls[i].flags_lock); + } + + return adpt; +} + +static void gamecube_adpt_destroy(struct gamecube_adapter* adpt) +{ + unsigned int i; + + for (i = 0; i < 4; i++) { + gamecube_ctrl_destroy(adpt->ctrls + i); + } + hid_hw_close(adpt->hdev); + hid_hw_stop(adpt->hdev); + kfree(adpt); +} + +/* This is needed, as by default the URB buffer size is set to 38, which is + * one byte too long and will result in EOVERFLOW failures. + */ +static int gamecube_fixup_urb_in(struct gamecube_adapter *adpt) +{ + struct hid_device *hdev = adpt->hdev; + struct usbhid_device *usbhid; + + if (!hid_is_using_ll_driver(hdev, &usb_hid_driver)) + return -EINVAL; + usbhid = hdev->driver_data; + if (usbhid->urbin->transfer_buffer_length < GC_INPUT_REPORT_SIZE) + return -EINVAL; + usbhid->urbin->transfer_buffer_length = GC_INPUT_REPORT_SIZE; + return 0; +} + +static int gamecube_hid_probe(struct hid_device *hdev, + const struct hid_device_id *id) +{ + struct gamecube_adapter *adpt; + int ret; + + adpt = gamecube_adpt_create(hdev); + if (!adpt) { + hid_err(hdev, "Can't alloc device\n"); + return -ENOMEM; + } + + 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; + } + + ret = gamecube_fixup_urb_in(adpt); + if (ret) { + hid_err(hdev, "failed to fix input URB\n"); + goto err_close; + } + + ret = gamecube_send_cmd_init(hdev); + if (ret < 0) { + hid_err(hdev, "failed to send init command\n"); + goto err_close; + } + + hid_info(hdev, "new adapter registered\n"); + return 0; + +err_close: + hid_hw_close(hdev); +err_stop: + hid_hw_stop(hdev); +err: + kfree(adpt); + return ret; +} + +static void gamecube_hid_remove(struct hid_device *hdev) +{ + struct gamecube_adapter *adpt = hid_get_drvdata(hdev); + + hid_info(hdev, "adapter removed\n"); + gamecube_adpt_destroy(adpt); +} + +static const struct hid_device_id gamecube_hid_devices[] = { + { HID_USB_DEVICE(USB_VENDOR_ID_NINTENDO, + USB_DEVICE_ID_NINTENDO_GAMECUBE_ADAPTER) }, + { } +}; +MODULE_DEVICE_TABLE(hid, gamecube_hid_devices); + +static struct hid_driver gamecube_hid_driver = { + .name = "gamecube-adapter", + .id_table = gamecube_hid_devices, + .probe = gamecube_hid_probe, + .remove = gamecube_hid_remove, + .raw_event = gamecube_hid_event, +}; +module_hid_driver(gamecube_hid_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("François-Xavier Carton "); +MODULE_DESCRIPTION("Driver for Nintendo Gamecube Controller Adapters"); diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index b18b13147a6f..1ebea811ea3b 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -882,6 +882,7 @@ #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_GAMECUBE_ADAPTER 0x0337 #define USB_VENDOR_ID_NOVATEK 0x0603 #define USB_DEVICE_ID_NOVATEK_PCT 0x0600 From patchwork Wed May 6 00:48:00 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Fran=C3=A7ois-Xavier_Carton?= X-Patchwork-Id: 11530109 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 9BF3415AB for ; Wed, 6 May 2020 00:46:26 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id 8426C206A5 for ; Wed, 6 May 2020 00:46:26 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="sDwhp5/v" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729678AbgEFAq0 (ORCPT ); Tue, 5 May 2020 20:46:26 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34976 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729657AbgEFAqZ (ORCPT ); Tue, 5 May 2020 20:46:25 -0400 Received: from mail-qk1-x743.google.com (mail-qk1-x743.google.com [IPv6:2607:f8b0:4864:20::743]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 50369C061A0F for ; Tue, 5 May 2020 17:46:24 -0700 (PDT) Received: by mail-qk1-x743.google.com with SMTP id n14so301068qke.8 for ; Tue, 05 May 2020 17:46:24 -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=yH6IUrcOkMLVUkK39uCSBsn0Q2jTsofq3xWsFnossNc=; b=sDwhp5/vx5Ey8OWWE4i9HSZuu2qtJP8bQeUsFb2D0fo9uZw19hQ9RskQ7m6Fw0groZ F46TT0LOqg9WAh/LPA9yYFLUt22GFJbj5jgPsPfgdiss+z/yUWh4iRacTr/ZKNX7EXOa B9eovFnP96hR4fIAWOA0N/T++WI2VpB4ylUjC3q3+y/ImsYUpGpkuxdywhfxXGruhmWf cyowU2jPDdH2Xe45PXvbODuCtf0vUs3Y4MBY2GBVKSptIjRbMrKVEfqnntuPJ0Uni0iR RWSf0Yq24PoU0N03N9IhoO5OBk7a6UVShF+SOUgEsQjxOQPGrwgkZ32aPWf416mDOgKq G9/g== 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=yH6IUrcOkMLVUkK39uCSBsn0Q2jTsofq3xWsFnossNc=; b=WQhyPIjoGkUJ/6ATgxKg7cu5dpEDWW0wNfNVNxHQ3/iGSmOHLocoob7Q1Skhwr6A5Z HFR8+mYAv1ruZZ0vnNSihnpPIbhnh8T26VCUGQxRqFGjCykkMqWdOpoELqmS5Eb0VCTH CEpSfLnJGXuDHaBHFhga3RfUHKqABCCcLMWOCprT+XQgwwgbYeWrDrPMTUb0XT243xLk LTLnjlgJv4fb4mBMHxj8h41IsPTb9VRmE/eGFDbWDzws44ralGdVl540Pfg2Ehxdpp1a auXaTHZlNmUiyc/SCsOlkkkrYSNwNzP1sbVkCUW9KFhv8zPA169GxhjUGBPxk+pOeUf4 Ig3w== X-Gm-Message-State: AGi0PubuJ/MeQ6LLjPBkgzYoTHCqclC3XEyyqDC/ASn8sezlMJKD3smG C53La/4+5sc2oEQGGiHuKTcAkrIO X-Google-Smtp-Source: APiQypKsSrn0LwCQcyCA/NFgRTlzDY2sL+uomgmBrOaxpllr4vf8Y1b7LGA1eC9s/tx3tPUP6ah27w== X-Received: by 2002:a05:620a:16d1:: with SMTP id a17mr6073606qkn.111.1588725983407; Tue, 05 May 2020 17:46:23 -0700 (PDT) Received: from localhost ([2601:483:4200:9113:fdae:121b:56a3:4870]) by smtp.gmail.com with ESMTPSA id a17sm488148qka.37.2020.05.05.17.46.23 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 05 May 2020 17:46:23 -0700 (PDT) From: =?utf-8?q?Fran=C3=A7ois-Xavier_Carton?= To: linux-input@vger.kernel.org Cc: =?utf-8?q?Fran=C3=A7ois-Xavier_Carton?= Subject: [PATCH 2/3] HID: gamecube-adapter: add rumble support Date: Wed, 6 May 2020 02:48:00 +0200 Message-Id: <20200506004801.9478-2-fx.carton91@gmail.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200506004801.9478-1-fx.carton91@gmail.com> References: <20200506004801.9478-1-fx.carton91@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 Add rumble support for the hid-gamecube-adapter driver. Rumble is reported with a single output report for all four controllers. Signed-off-by: François-Xavier Carton --- drivers/hid/Kconfig | 8 ++++ drivers/hid/hid-gamecube-adapter.c | 61 +++++++++++++++++++++++++++++- 2 files changed, 68 insertions(+), 1 deletion(-) diff --git a/drivers/hid/Kconfig b/drivers/hid/Kconfig index d49e261a74f6..324981308783 100644 --- a/drivers/hid/Kconfig +++ b/drivers/hid/Kconfig @@ -360,6 +360,14 @@ config HID_GAMECUBE_ADAPTER To compile this driver as a module, choose M here: the module will be called hid-gamecube-adapter. +config HID_GAMECUBE_ADAPTER_FF + bool "Nintendo Gamecube Controller Adapter force feedback" + depends on HID_GAMECUBE_ADAPTER + select INPUT_FF_MEMLESS + ---help--- + Say Y here if you want to enable force feedback support for Nintendo + Gamecube Controller Adapters. + config HID_GEMBIRD tristate "Gembird Joypad" depends on HID diff --git a/drivers/hid/hid-gamecube-adapter.c b/drivers/hid/hid-gamecube-adapter.c index 0537ece0979a..b4022ff5b4b4 100644 --- a/drivers/hid/hid-gamecube-adapter.c +++ b/drivers/hid/hid-gamecube-adapter.c @@ -20,7 +20,8 @@ #include "usbhid/usbhid.h" enum gamecube_output { - GC_CMD_INIT = 0x13 + GC_CMD_INIT = 0x13, + GC_CMD_RUMBLE = 0x11 }; enum gamecube_input { @@ -54,14 +55,17 @@ enum gamecube_btn { struct gamecube_ctrl { struct input_dev __rcu *input; enum gamecube_ctrl_flags flags; + u8 rumble; struct gamecube_adapter *adpt; struct work_struct work_connect; spinlock_t flags_lock; + spinlock_t rumble_lock; }; struct gamecube_adapter { struct gamecube_ctrl ctrls[4]; struct hid_device *hdev; + struct work_struct work_rumble; }; static int gamecube_hid_send(struct hid_device *hdev, const u8 *data, size_t n) @@ -83,6 +87,49 @@ static int gamecube_send_cmd_init(struct hid_device *hdev) return gamecube_hid_send(hdev, initcmd, sizeof(initcmd)); } +#ifdef CONFIG_HID_GAMECUBE_ADAPTER_FF +static int gamecube_send_cmd_rumble(struct hid_device *hdev, struct gamecube_ctrl *ctrls) +{ + u8 cmd[5] = {GC_CMD_RUMBLE}; + unsigned long flags; + unsigned int i; + + for (i = 0; i < 4; i++) { + if (!(ctrls[i].flags & GC_TYPES) || !(ctrls[i].flags & GC_FLAG_EXTRAPOWER)) + continue; + spin_lock_irqsave(&ctrls[i].rumble_lock, flags); + cmd[i + 1] = ctrls[i].rumble; + spin_unlock_irqrestore(&ctrls[i].rumble_lock, flags); + } + return gamecube_hid_send(hdev, cmd, sizeof(cmd)); +} + +static void gamecube_rumble_worker(struct work_struct *work) +{ + struct gamecube_adapter *adpt = container_of(work, struct gamecube_adapter, + work_rumble); + + gamecube_send_cmd_rumble(adpt->hdev, adpt->ctrls); +} + +static int gamecube_rumble_play(struct input_dev *dev, void *data, + struct ff_effect *eff) +{ + struct gamecube_ctrl *ctrl = input_get_drvdata(dev); + struct gamecube_adapter *adpt = ctrl->adpt; + unsigned long flags; + + if (eff->type != FF_RUMBLE) + return 0; + + spin_lock_irqsave(&ctrl->rumble_lock, flags); + ctrl->rumble = (eff->u.rumble.strong_magnitude || eff->u.rumble.weak_magnitude); + spin_unlock_irqrestore(&ctrl->rumble_lock, flags); + schedule_work(&adpt->work_rumble); + return 0; +} +#endif + static const unsigned int gamecube_buttons[] = { BTN_START, BTN_TR2, BTN_TR, BTN_TL, BTN_SOUTH, BTN_WEST, BTN_EAST, BTN_NORTH, @@ -134,6 +181,11 @@ static int gamecube_ctrl_create(struct gamecube_ctrl *ctrl) input_set_capability(input, EV_KEY, gamecube_buttons[i]); for (i = 0; i < ARRAY_SIZE(gamecube_axes); i++) input_set_abs_params(input, gamecube_axes[i], 0, 255, 0, 0); +#ifdef CONFIG_HID_GAMECUBE_ADAPTER_FF + input_set_capability(input, EV_FF, FF_RUMBLE); + if (input_ff_create_memless(input, NULL, gamecube_rumble_play)) + hid_warn(hdev, "failed to create ff memless\n"); +#endif ret = input_register_device(input); if (ret) @@ -269,7 +321,11 @@ static struct gamecube_adapter* gamecube_adpt_create(struct hid_device *hdev) adpt->ctrls[i].adpt = adpt; INIT_WORK(&adpt->ctrls[i].work_connect, gamecube_work_connect_cb); spin_lock_init(&adpt->ctrls[i].flags_lock); + spin_lock_init(&adpt->ctrls[i].rumble_lock); } +#ifdef CONFIG_HID_GAMECUBE_ADAPTER_FF + INIT_WORK(&adpt->work_rumble, gamecube_rumble_worker); +#endif return adpt; } @@ -281,6 +337,9 @@ static void gamecube_adpt_destroy(struct gamecube_adapter* adpt) for (i = 0; i < 4; i++) { gamecube_ctrl_destroy(adpt->ctrls + i); } +#ifdef CONFIG_HID_GAMECUBE_ADAPTER_FF + cancel_work_sync(&adpt->work_rumble); +#endif hid_hw_close(adpt->hdev); hid_hw_stop(adpt->hdev); kfree(adpt); From patchwork Wed May 6 00:48:01 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: =?utf-8?q?Fran=C3=A7ois-Xavier_Carton?= X-Patchwork-Id: 11530111 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 BEE8581 for ; Wed, 6 May 2020 00:46:26 +0000 (UTC) Received: from vger.kernel.org (vger.kernel.org [23.128.96.18]) by mail.kernel.org (Postfix) with ESMTP id A7DC320752 for ; Wed, 6 May 2020 00:46:26 +0000 (UTC) Authentication-Results: mail.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="WCHPKK2N" Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1729657AbgEFAq0 (ORCPT ); Tue, 5 May 2020 20:46:26 -0400 Received: from lindbergh.monkeyblade.net ([23.128.96.19]:34984 "EHLO lindbergh.monkeyblade.net" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1729677AbgEFAqZ (ORCPT ); Tue, 5 May 2020 20:46:25 -0400 Received: from mail-qt1-x841.google.com (mail-qt1-x841.google.com [IPv6:2607:f8b0:4864:20::841]) by lindbergh.monkeyblade.net (Postfix) with ESMTPS id 9A595C061A10 for ; Tue, 5 May 2020 17:46:25 -0700 (PDT) Received: by mail-qt1-x841.google.com with SMTP id g16so35947qtp.11 for ; Tue, 05 May 2020 17:46:25 -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=TJ3Z5URSGRj1N2bEPpJHBNw4yHLCdrfa3AgxTKKb1i4=; b=WCHPKK2NsujLVk/cT6IMSGMyL3PJVOvlX7nha8gtNkGi7kIxk1QygVuNvKDWOyLuma Z/2yan8ZQeR2W10ls++dYrxBao2AUvKxoRedit2L7mTvRd8fxtuEGqQeCpUX8ugROdjo fVO6jPpmjuZ9UMaU9LzdqQDbov+GwwFzwymRVIO1QZrT+HXcgqCGtyRGT6kXXhElQ6Yk jGSkKRLYCgA+PZ2uy+MJP75SI32a90bq4QAeEFR/m+C/I7RpeZ1XbHZta/XhuGwsujQB F5ZcV4HnZ2gQlnaLAAr3CdedD+L3S07OVZHD1kKL5UowR/KSfTX8G7euwSnk0sOhLkh2 VsAw== 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=TJ3Z5URSGRj1N2bEPpJHBNw4yHLCdrfa3AgxTKKb1i4=; b=sVX4VrbvkFeTWrawcAgdW7oSYru97IKyjrZVUB4jq1HSQlkv6d8oOn89M62IuQdQvX 2MKXVnS6wweyM6YjFm956g8o35orvyE/JC0DXIX/2Dd/WDx9ylVGQE7Tj7VJYLf4DgCS 3xm7+hDs1sgha5MbPRD0YiFxmgk2QZohMNTTaEHvxdfxkztFuYTB6RLWcw1OKv3PDQDb nGrYCtMzK1SBDXoquLOvdPKv947QDpRt2hc+TYNPFolZrTjUm36VJz/TesVsSsh4jqZz h+zSiisCqZaN8669qhzkn6rxAL0yHCA4MSAmo1nNMlkrtfPQCAMNZpwSDJ5M5FK/AJdo /geg== X-Gm-Message-State: AGi0PuawrrMw7V0rDRanEila8VubLKh+9G7FFaHwRmUFC/2h9+k+rt6A HNbfuuCidPfZgedWM9p1aI5K9Uay X-Google-Smtp-Source: APiQypKJ+HG/KGfWVpAyAUbUMDsVyqdRiMGj+gRMG1o1SVcmYGy5Kw2HoE0McKQuYON1KOWhTpIhmg== X-Received: by 2002:aed:374a:: with SMTP id i68mr5598938qtb.69.1588725984763; Tue, 05 May 2020 17:46:24 -0700 (PDT) Received: from localhost ([2601:483:4200:9113:fdae:121b:56a3:4870]) by smtp.gmail.com with ESMTPSA id k2sm124009qte.16.2020.05.05.17.46.24 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Tue, 05 May 2020 17:46:24 -0700 (PDT) From: =?utf-8?q?Fran=C3=A7ois-Xavier_Carton?= To: linux-input@vger.kernel.org Cc: =?utf-8?q?Fran=C3=A7ois-Xavier_Carton?= Subject: [PATCH 3/3] HID: gamecube-adapter: make axis limits parameters Date: Wed, 6 May 2020 02:48:01 +0200 Message-Id: <20200506004801.9478-3-fx.carton91@gmail.com> X-Mailer: git-send-email 2.26.2 In-Reply-To: <20200506004801.9478-1-fx.carton91@gmail.com> References: <20200506004801.9478-1-fx.carton91@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 axes do not cover the full 0-255 range, with different limit values for each axis. The limits are made module parameters so they can be configured. Signed-off-by: François-Xavier Carton --- drivers/hid/hid-gamecube-adapter.c | 36 +++++++++++++++++++++++++----- 1 file changed, 30 insertions(+), 6 deletions(-) diff --git a/drivers/hid/hid-gamecube-adapter.c b/drivers/hid/hid-gamecube-adapter.c index b4022ff5b4b4..6a2fb908de3c 100644 --- a/drivers/hid/hid-gamecube-adapter.c +++ b/drivers/hid/hid-gamecube-adapter.c @@ -19,6 +19,30 @@ #include #include "usbhid/usbhid.h" +static unsigned int left_stick_min = 0; +module_param(left_stick_min, uint, 0644); +MODULE_PARM_DESC(left_stick_min, "Minimum value for left stick."); + +static unsigned int left_stick_max = 255; +module_param(left_stick_max, uint, 0644); +MODULE_PARM_DESC(left_stick_max, "Maximum value for left stick."); + +static unsigned int right_stick_min = 0; +module_param(right_stick_min, uint, 0644); +MODULE_PARM_DESC(right_stick_min, "Minimum value for right stick (C stick)."); + +static unsigned int right_stick_max = 255; +module_param(right_stick_max, uint, 0644); +MODULE_PARM_DESC(right_stick_max, "Maximum value for right stick (C stick)."); + +static unsigned int shoulder_min = 0; +module_param(shoulder_min, uint, 0644); +MODULE_PARM_DESC(shoulder_min, "Minimum value for shoulders."); + +static unsigned int shoulder_max = 255; +module_param(shoulder_max, uint, 0644); +MODULE_PARM_DESC(shoulder_max, "Maximum value for shoulders."); + enum gamecube_output { GC_CMD_INIT = 0x13, GC_CMD_RUMBLE = 0x11 @@ -136,10 +160,6 @@ static const unsigned int gamecube_buttons[] = { BTN_DPAD_LEFT, BTN_DPAD_RIGHT, BTN_DPAD_DOWN, BTN_DPAD_UP }; -static const unsigned int gamecube_axes[] = { - ABS_X, ABS_Y, ABS_RX, ABS_RY, ABS_Z, ABS_RZ -}; - static const char* gamecube_ctrl_name(enum gamecube_ctrl_flags flags) { switch (flags & GC_TYPES) { @@ -179,8 +199,12 @@ static int gamecube_ctrl_create(struct gamecube_ctrl *ctrl) for (i = 0; i < ARRAY_SIZE(gamecube_buttons); i++) input_set_capability(input, EV_KEY, gamecube_buttons[i]); - for (i = 0; i < ARRAY_SIZE(gamecube_axes); i++) - input_set_abs_params(input, gamecube_axes[i], 0, 255, 0, 0); + input_set_abs_params(input, ABS_X, left_stick_min, left_stick_max, 0, 0); + input_set_abs_params(input, ABS_Y, left_stick_min, left_stick_max, 0, 0); + input_set_abs_params(input, ABS_RX, right_stick_min, right_stick_max, 0, 0); + input_set_abs_params(input, ABS_RY, right_stick_min, right_stick_max, 0, 0); + input_set_abs_params(input, ABS_Z, shoulder_min, shoulder_max, 0, 0); + input_set_abs_params(input, ABS_RZ, shoulder_min, shoulder_max, 0, 0); #ifdef CONFIG_HID_GAMECUBE_ADAPTER_FF input_set_capability(input, EV_FF, FF_RUMBLE); if (input_ff_create_memless(input, NULL, gamecube_rumble_play))