From nobody Mon Jun 8 17:56:02 2026 Received: from mail-vk1-f193.google.com (mail-vk1-f193.google.com [209.85.221.193]) (using TLSv1.2 with cipher ECDHE-RSA-AES128-GCM-SHA256 (128/128 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id BDC2137F721 for ; Wed, 27 May 2026 16:02:21 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=209.85.221.193 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779897743; cv=none; b=AsvrfesLllJi2Ls/HFyauAQkvP6yG7qkNKlAxYD0iIZVLD/8vQtdn+NC8N2nIyW1ENluk+cbko9wHnrr6R7Zcrnl/RKnAYtW+0bAyABSMii9c0wHYWnnvGPpS01kv4hwArM9HoP+DY5f5VOh0CGj+7WIEIMrYu08Vw85RhjI4qA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1779897743; c=relaxed/simple; bh=hPbkxEPsLQNL0Mx05iqP8Ul/D5nEMWOuY0GVzpSSKV0=; h=From:Date:Subject:MIME-Version:Content-Type:Message-Id:To:Cc; b=Dh49toHmFaj1we1CqUVUsBcv1ILyBVf83k1fT/aN3ZFl/88bvNtYSup8A+mQ9Gk+Pejp5HfsbWbzaz2rPQvwAl99lIAwtEOp5YMRnFfkClvfgvzZrQUEU6MArltu6bfCNQcrxO25pnobEXs6cWoIq9OGIwTkyd0DDLNbtyjx6ZM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=hectorzelaya.dev; spf=none smtp.mailfrom=hectorzelaya.dev; dkim=pass (2048-bit key) header.d=hectorzelaya.dev header.i=@hectorzelaya.dev header.b=c5ZirpaD; arc=none smtp.client-ip=209.85.221.193 Authentication-Results: smtp.subspace.kernel.org; dmarc=none (p=none dis=none) header.from=hectorzelaya.dev Authentication-Results: smtp.subspace.kernel.org; spf=none smtp.mailfrom=hectorzelaya.dev Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=hectorzelaya.dev header.i=@hectorzelaya.dev header.b="c5ZirpaD" Received: by mail-vk1-f193.google.com with SMTP id 71dfb90a1353d-5774680983dso4194031e0c.0 for ; Wed, 27 May 2026 09:02:21 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=hectorzelaya.dev; s=google; t=1779897741; x=1780502541; darn=vger.kernel.org; h=cc:to:message-id:content-transfer-encoding:mime-version:subject :date:from:from:to:cc:subject:date:message-id:reply-to; bh=8wgdYARPJ/fdedNZjsQGQkNvoyDqNmjFg1FjkoyjLQM=; b=c5ZirpaDQL5ioAHOwDsoD1H25PYjWdMyvJA+50ZJJkk4oEpQNTVBlzvGuEzq8zvsR0 LhX2Ks4AsP+Fpeyh7k1yOwtJFeSNkGpqDe7J00TKqblg/IRyZMWwbrhNY2n4RvSKhp8d msP1RvsrSXndPwrRodsbg7VhYudxerYgViz2P/Dm2QyYLP+K3W7+99+cXjZG2XFkDCEk 7OFMHjzf9N9d8mwjEQiX44cUi0XVZLH4vE+lmWKyFnRA6JQF073UxGQsZKVvgom5dgtz f4RyD0bxSxlC38X/f8HmYXJIuSZSRog3v5fo0rl5+2UHj/tF+0uyRQvv6MnBlUAvY28a 9gmA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1779897741; x=1780502541; h=cc:to:message-id:content-transfer-encoding:mime-version:subject :date:from:x-gm-gg:x-gm-message-state:from:to:cc:subject:date :message-id:reply-to; bh=8wgdYARPJ/fdedNZjsQGQkNvoyDqNmjFg1FjkoyjLQM=; b=IfL5JQIT5v4eEvfxx5mfQ6wGg5hCFQJzrOis8wy8jFrj3elMmrpigNNnAhkQfKoM8E Jox7/1iiRWYXBOhMeNx9eZ5g4WBsSj4tEfOESFQr2kp9tnZXROPmaONRfUClUdyXi9lY sXBDmL13Vm/bmoWIrGT1Fty4voncDZW+tESoIIHC2ZurvJ6KDTLPCv2p14YuC0yCKYgX C7nqU/VgfPY3a4sQpiZWw7rI8NP9T5bzpTaa4LS7ypzM77dP0D/Ff260A0wZZT8Kg2WB TYxpwkfQ29xWusiLna6EXFID8+ejEKgEbwAwuX+Yg7GLaeBrDC9S5mhR8T/u8ZxELxYA /M2g== X-Forwarded-Encrypted: i=1; AFNElJ8OwTfjnWEcN/TWL1fTbKQCviYfYEfQViTTgsb5Y1+yadiaGL6wotdrZgAlCvuze7vg0SnoOjgg2KJeDxQ=@vger.kernel.org X-Gm-Message-State: AOJu0Yy1zGufmbd4bIgBYL1aARXzKBByzmY89yqJRtwQByJnKYRQagbH /lWw78CvbbyQiuzspfgB7bgFOpiTV788c72bCbUP4z6wlFWeBqjFa3wPwv7YdyULOVVDu9SgQJs T1BvdxJB7oAQIznzc X-Gm-Gg: Acq92OEOvKOdlDigKKjs1hEmN3skx45BYO0rOIbj+W3L1gqBLrB7UEPhb6WXaftLaQ1 E0P5Io7ivLiZFhcZ/trORvfPk3JrHKxjwCK/unKaGHxi+MPNBhnRUIRWyJyCZ4E9IZ6bJxuDHbI CQStCJ9VkooQ1rZ27/o2R0tPU855ZpX6NH0KgENqqazL6kxNRAOiGIH8vZpvXDjQCyFW0t95l4D Whqt9FOIXaSDhlSAE8XZa6koKeniyVYGw/jdAGLZfSgufkdoDjP6l7h3MBDJzjTc7ovRmLTWTfJ jhgoDSSb1F1fl8tdA8s4Fr5HLjEdLw3jzhT063VGKMBKKG+PJMXSJQFrnkkP9QeJPHJyfylyxsR mQGWlJztZdBFeAGumBzWOYReffDFTxDghQ5xKXBAYwkfaY1a8G62yb8OzS6mVarwcss0+/Y8g/d QoL5CHGxOOd0pGTyDw+CL+0iEwJEZPQnDcz7gTocNBJvnyd8V1zw== X-Received: by 2002:a05:6122:2407:b0:575:e9eb:d879 with SMTP id 71dfb90a1353d-58659010a93mr11862865e0c.0.1779897739402; Wed, 27 May 2026 09:02:19 -0700 (PDT) Received: from [127.0.0.2] ([190.150.67.183]) by smtp.gmail.com with ESMTPSA id a1e0cc1a2514c-96196396953sm12635348241.1.2026.05.27.09.02.17 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 27 May 2026 09:02:18 -0700 (PDT) From: Hector Zelaya Date: Wed, 27 May 2026 10:01:32 -0600 Subject: [PATCH v2] HID: nintendo: add support for HORI Wireless Switch Pad Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Type: text/plain; charset="utf-8" Content-Transfer-Encoding: quoted-printable Message-Id: <20260527-hori-support-v2-1-195c2f832112@hectorzelaya.dev> X-B4-Tracking: v=1; b=H4sIAFsVF2oC/3WNQQrCMBBFr1JmbSQNGoor7yFdpMloRqQpkzZYS +5uUtcyf/Pg/zcbRGTCCJdmA8ZEkcJYQB0asN6MDxTkCoOSSsuz0sIHJhGXaQo8C9kNJdacpOu gTCbGO7133a3/cVyGJ9q5OmrDU5wDr/u/1NbeH3VqRblOt1aawSqUV180gT/4Mqs5OkzQ55y/o /RMgcEAAAA= X-Change-ID: 20260526-hori-support-08b08bca40d8 To: Jiri Kosina , Benjamin Tissoires , "Daniel J. Ogorchock" Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Hector Zelaya , Joshua Peisach X-Mailer: b4 0.15.2 Add support for the HORI Wireless Switch Pad (vendor 0x0f0d, product 0x00f6), a licensed third-party Nintendo Switch Pro Controller. The controller reports controller type 0x06 (vs 0x03 for first-party Pro Controllers) and has the following quirks: - SPI flash calibration data is incompatible; use default stick calibration values instead. - X and Y button bits are swapped compared to first-party controllers; add a dedicated button mapping table. - Rumble and IMU enable may timeout (no vibration motor in hardware); treat as non-fatal for licensed controllers. Tested over Bluetooth on NixOS with kernel 7.0.5 and 7.0.10: - All 14 buttons map correctly - Player LED sets on connect - Sticks report correctly with default calibration - IMU/gyro data streams at 60Hz - D-pad reports on ABS_HAT0X/HAT0Y Device information: Bluetooth name: Lic Pro Controller Bluetooth HID: 0005:0F0D:00F6 Assisted-by: Kiro:Auto [Amazon Kiro IDE] Signed-off-by: Hector Zelaya Reviewed-by: Joshua Peisach --- Changes in v2: - Clear ret =3D 0 after non-fatal IMU/rumble timeouts to prevent unintentional probe failure. - Link to v1: https://patch.msgid.link/20260526-hori-support-v1-1-1861c0abc= 2e0@hectorzelaya.dev To: Jiri Kosina To: Benjamin Tissoires To: "Daniel J. Ogorchock" Cc: linux-input@vger.kernel.org Cc: linux-kernel@vger.kernel.org --- drivers/hid/hid-ids.h | 3 ++ drivers/hid/hid-nintendo.c | 80 +++++++++++++++++++++++++++++++++++++++---= ---- 2 files changed, 71 insertions(+), 12 deletions(-) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index a1cfa436344a..3b0767cc47fd 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -683,6 +683,9 @@ #define USB_DEVICE_ID_HARMONIX_WII_RB3_KEYBOARD 0x3330 #define USB_DEVICE_ID_HARMONIX_WII_RB3_MPA_KEYBOARD_MODE 0x3338 =20 +#define USB_VENDOR_ID_HORI 0x0f0d +#define USB_DEVICE_ID_HORI_WIRELESS_SWITCH_PAD 0x00f6 + #define USB_VENDOR_ID_HP 0x03f0 #define USB_PRODUCT_ID_HP_ELITE_PRESENTER_MOUSE_464A 0x464a #define USB_PRODUCT_ID_HP_LOGITECH_OEM_USB_OPTICAL_MOUSE_0A4A 0x0a4a diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c index 29008c2cc530..2d37ddeffdb6 100644 --- a/drivers/hid/hid-nintendo.c +++ b/drivers/hid/hid-nintendo.c @@ -316,6 +316,7 @@ enum joycon_ctlr_type { JOYCON_CTLR_TYPE_JCL =3D 0x01, JOYCON_CTLR_TYPE_JCR =3D 0x02, JOYCON_CTLR_TYPE_PRO =3D 0x03, + JOYCON_CTLR_TYPE_LIC_PRO =3D 0x06, JOYCON_CTLR_TYPE_NESL =3D 0x09, JOYCON_CTLR_TYPE_NESR =3D 0x0A, JOYCON_CTLR_TYPE_SNES =3D 0x0B, @@ -433,6 +434,25 @@ static const struct joycon_ctlr_button_mapping procon_= button_mappings[] =3D { { /* sentinel */ }, }; =20 +/* Licensed Pro Controllers (e.g. HORI) swap X/Y bits in the report */ +static const struct joycon_ctlr_button_mapping lic_procon_button_mappings[= ] =3D { + { BTN_EAST, JC_BTN_A, }, + { BTN_SOUTH, JC_BTN_B, }, + { BTN_NORTH, JC_BTN_Y, }, + { BTN_WEST, JC_BTN_X, }, + { BTN_TL, JC_BTN_L, }, + { BTN_TR, JC_BTN_R, }, + { BTN_TL2, JC_BTN_ZL, }, + { BTN_TR2, JC_BTN_ZR, }, + { BTN_SELECT, JC_BTN_MINUS, }, + { BTN_START, JC_BTN_PLUS, }, + { BTN_THUMBL, JC_BTN_LSTICK, }, + { BTN_THUMBR, JC_BTN_RSTICK, }, + { BTN_MODE, JC_BTN_HOME, }, + { BTN_Z, JC_BTN_CAP, }, + { /* sentinel */ }, +}; + static const struct joycon_ctlr_button_mapping nescon_button_mappings[] = =3D { { BTN_SOUTH, JC_BTN_A, }, { BTN_EAST, JC_BTN_B, }, @@ -695,7 +715,8 @@ static inline bool joycon_type_is_right_joycon(struct j= oycon_ctlr *ctlr) =20 static inline bool joycon_type_is_procon(struct joycon_ctlr *ctlr) { - return ctlr->ctlr_type =3D=3D JOYCON_CTLR_TYPE_PRO; + return ctlr->ctlr_type =3D=3D JOYCON_CTLR_TYPE_PRO || + ctlr->ctlr_type =3D=3D JOYCON_CTLR_TYPE_LIC_PRO; } =20 static inline bool joycon_type_is_snescon(struct joycon_ctlr *ctlr) @@ -1710,7 +1731,10 @@ static void joycon_parse_report(struct joycon_ctlr *= ctlr, joycon_report_left_stick(ctlr, rep); joycon_report_right_stick(ctlr, rep); joycon_report_dpad(ctlr, rep); - joycon_report_buttons(ctlr, rep, procon_button_mappings); + if (ctlr->ctlr_type =3D=3D JOYCON_CTLR_TYPE_LIC_PRO) + joycon_report_buttons(ctlr, rep, lic_procon_button_mappings); + else + joycon_report_buttons(ctlr, rep, procon_button_mappings); } else if (joycon_type_is_any_nescon(ctlr)) { joycon_report_dpad(ctlr, rep); joycon_report_buttons(ctlr, rep, nescon_button_mappings); @@ -2156,7 +2180,10 @@ static int joycon_input_create(struct joycon_ctlr *c= tlr) joycon_config_left_stick(ctlr->input); joycon_config_right_stick(ctlr->input); joycon_config_dpad(ctlr->input); - joycon_config_buttons(ctlr->input, procon_button_mappings); + if (ctlr->ctlr_type =3D=3D JOYCON_CTLR_TYPE_LIC_PRO) + joycon_config_buttons(ctlr->input, lic_procon_button_mappings); + else + joycon_config_buttons(ctlr->input, procon_button_mappings); } else if (joycon_type_is_any_nescon(ctlr)) { joycon_config_dpad(ctlr->input); joycon_config_buttons(ctlr->input, nescon_button_mappings); @@ -2503,13 +2530,30 @@ static int joycon_init(struct hid_device *hdev) =20 if (joycon_has_joysticks(ctlr)) { /* get controller calibration data, and parse it */ - ret =3D joycon_request_calibration(ctlr); - if (ret) { + if (ctlr->ctlr_type =3D=3D JOYCON_CTLR_TYPE_LIC_PRO) { /* - * We can function with default calibration, but it may be - * inaccurate. Provide a warning, and continue on. + * Licensed controllers may have incompatible SPI flash + * layouts. Use default calibration values. */ - hid_warn(hdev, "Analog stick positions may be inaccurate\n"); + hid_info(hdev, "using default cal for licensed controller\n"); + joycon_use_default_calibration(hdev, + &ctlr->left_stick_cal_x, + &ctlr->left_stick_cal_y, + "left", 0); + joycon_use_default_calibration(hdev, + &ctlr->right_stick_cal_x, + &ctlr->right_stick_cal_y, + "right", 0); + } else { + ret =3D joycon_request_calibration(ctlr); + if (ret) { + /* + * We can function with default calibration, but + * it may be inaccurate. Provide a warning, and + * continue on. + */ + hid_warn(hdev, "Analog stick positions may be inaccurate\n"); + } } } =20 @@ -2527,8 +2571,13 @@ static int joycon_init(struct hid_device *hdev) /* Enable the IMU */ ret =3D joycon_enable_imu(ctlr); if (ret) { - hid_err(hdev, "Failed to enable the IMU; ret=3D%d\n", ret); - goto out_unlock; + if (ctlr->ctlr_type =3D=3D JOYCON_CTLR_TYPE_LIC_PRO) { + hid_dbg(hdev, "IMU enable failed for licensed controller, continuing\n= "); + ret =3D 0; + } else { + hid_err(hdev, "Failed to enable the IMU; ret=3D%d\n", ret); + goto out_unlock; + } } } =20 @@ -2543,8 +2592,13 @@ static int joycon_init(struct hid_device *hdev) /* Enable rumble */ ret =3D joycon_enable_rumble(ctlr); if (ret) { - hid_err(hdev, "Failed to enable rumble; ret=3D%d\n", ret); - goto out_unlock; + if (ctlr->ctlr_type =3D=3D JOYCON_CTLR_TYPE_LIC_PRO) { + hid_dbg(hdev, "rumble enable failed for licensed controller, continuin= g\n"); + ret =3D 0; + } else { + hid_err(hdev, "Failed to enable rumble; ret=3D%d\n", ret); + goto out_unlock; + } } } =20 @@ -2813,6 +2867,8 @@ static const struct hid_device_id nintendo_hid_device= s[] =3D { USB_DEVICE_ID_NINTENDO_GENCON) }, { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_NINTENDO, USB_DEVICE_ID_NINTENDO_N64CON) }, + { HID_BLUETOOTH_DEVICE(USB_VENDOR_ID_HORI, + USB_DEVICE_ID_HORI_WIRELESS_SWITCH_PAD) }, { } }; MODULE_DEVICE_TABLE(hid, nintendo_hid_devices); --- base-commit: e71bac24ec1f517f399a9eb471255b8f1c330b93 change-id: 20260526-hori-support-08b08bca40d8 Best regards, -- =20 Hector Zelaya