From mboxrd@z Thu Jan 1 00:00:00 1970 Received: from endrift.com (endrift.com [173.255.198.10]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id D8418352020 for ; Wed, 1 Jul 2026 03:16:19 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=173.255.198.10 ARC-Seal:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1782875781; cv=none; b=cRP4Mdnv8cHgNMjiyyAbA8hc37JkcKjJZ9SLtOoPHQPn8RKtefxg9hxMROQxDEL88edVuiIHGolissQ2wHBp5r1h2/fO2sZqjjCurFYxv3mKfG62cgXsCMmlxQrhFmA9EH9zSRfiVTmGJJFAExXTt1I6g2s/Dp0NDb6lglG+xbo= ARC-Message-Signature:i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1782875781; c=relaxed/simple; bh=wZM68orz66jq0n64EFmanYrm2WVNVz2BIpzo+/pe3UI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Mc74dLEj53sIQBQ25M8AP7H9E7hqi/40YKfSAb4gEskGfX4Wn+7+ozD86tobKXWTB8cXhzBzd0nT++sVwlinVRBHqYIkDoFaWXRGSRtQs+5XKz1tRNSAkCP4D02ZeDN1EBuR+U48NhqHGG+6f3JeOiqq8quorL9y0IMvmB2HrmY= ARC-Authentication-Results:i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=endrift.com; spf=pass smtp.mailfrom=endrift.com; dkim=pass (2048-bit key) header.d=endrift.com header.i=@endrift.com header.b=U9ebfsRP; arc=none smtp.client-ip=173.255.198.10 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=endrift.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=endrift.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=endrift.com header.i=@endrift.com header.b="U9ebfsRP" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=endrift.com; s=2020; t=1782875779; bh=wZM68orz66jq0n64EFmanYrm2WVNVz2BIpzo+/pe3UI=; h=From:To:Cc:Subject:Date:In-Reply-To:References:From; b=U9ebfsRPMDhU40f4bL3R955UxBrquFLz7zwXIRKvfnUpIrQC5+z18YiIu3Xv1EEc5 nMcQulJ3JqGXMBkARA4KthqB53WrR89M534gGZ9Ya22rTRkAvP0i7xJb2KircOdTrt 38d96vKvm58zzNCXWqeTvpNQiSMyfYt8VZU2L+CAFk6iFX6Vwn8VQHHGQRyex4JIB0 0JOs2T0uVu0plhzh1LQ1wKwZairr73PhhC0zNLdJwk/bqogB5uqFVRd6cZai4IHTDd iSw3fP0ytHJlpScoOvRjRES/4xQrDgfYQcQXkSuefAiSSwFfEzgRwpB810OWHoimho kFCA6QPMpxRdw== Received: from microtis.vulpes.eutheria.net (71-212-73-87.tukw.qwest.net [71.212.73.87]) by endrift.com (Postfix) with ESMTPSA id 1A59AA099; Tue, 30 Jun 2026 20:16:19 -0700 (PDT) From: Vicki Pfau To: Dmitry Torokhov , Jiri Kosina , Benjamin Tissoires , linux-input@vger.kernel.org Cc: Vicki Pfau , Silvan Jegen Subject: [PATCH v8 3/3] HID: nintendo: Add unified report format support Date: Tue, 30 Jun 2026 20:15:11 -0700 Message-ID: <20260701031513.3068035-4-vi@endrift.com> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260701031513.3068035-1-vi@endrift.com> References: <20260701031513.3068035-1-vi@endrift.com> Precedence: bulk X-Mailing-List: linux-input@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: 8bit This adds support for the "unified" report format that all controllers also support, which has overlapping fields for like buttons and axes between them. Signed-off-by: Vicki Pfau --- drivers/hid/hid-nintendo.c | 151 +++++++++++++++++++++++++++++++++++-- 1 file changed, 146 insertions(+), 5 deletions(-) diff --git a/drivers/hid/hid-nintendo.c b/drivers/hid/hid-nintendo.c index 00ab4bee3dab..0bddfd84fd17 100644 --- a/drivers/hid/hid-nintendo.c +++ b/drivers/hid/hid-nintendo.c @@ -2872,6 +2872,36 @@ static int joycon_suspend(struct hid_device *hdev, pm_message_t message) #define NS2_BTN3_SR BIT(6) #define NS2_BTN3_SL BIT(7) +#define NS2_BTN_U1_Y BIT(0) +#define NS2_BTN_U1_X BIT(1) +#define NS2_BTN_U1_B BIT(2) +#define NS2_BTN_U1_A BIT(3) +#define NS2_BTN_U1_SR BIT(4) +#define NS2_BTN_U1_SL BIT(5) +#define NS2_BTN_U1_R BIT(6) +#define NS2_BTN_U1_ZR BIT(7) + +#define NS2_BTN_U2_MINUS BIT(0) +#define NS2_BTN_U2_PLUS BIT(1) +#define NS2_BTN_U2_RS BIT(2) +#define NS2_BTN_U2_LS BIT(3) +#define NS2_BTN_U2_HOME BIT(4) +#define NS2_BTN_U2_CAPTURE BIT(5) +#define NS2_BTN_U2_C BIT(6) + +#define NS2_BTN_U3_DOWN BIT(0) +#define NS2_BTN_U3_UP BIT(1) +#define NS2_BTN_U3_RIGHT BIT(2) +#define NS2_BTN_U3_LEFT BIT(3) +#define NS2_BTN_U3_SR BIT(4) +#define NS2_BTN_U3_SL BIT(5) +#define NS2_BTN_U3_L BIT(6) +#define NS2_BTN_U3_ZL BIT(7) + +#define NS2_BTN_U4_GR BIT(0) +#define NS2_BTN_U4_GL BIT(1) +#define NS2_BTN_U4_HEADSET BIT(5) + #define NS2_BTN_JCR_HOME BIT(0) #define NS2_BTN_JCR_GR BIT(2) #define NS2_BTN_JCR_C NS2_BTN3_C @@ -3120,6 +3150,22 @@ static const struct switch2_ctlr_button_mapping ns2_left_joycon_button_mappings[ { /* sentinel */ }, }; +static const struct switch2_ctlr_button_mapping ns2_left_joycon_button_unified_mappings[] = { + { BTN_DPAD_LEFT, 2, NS2_BTN_U3_LEFT, }, + { BTN_DPAD_UP, 2, NS2_BTN_U3_UP, }, + { BTN_DPAD_DOWN, 2, NS2_BTN_U3_DOWN, }, + { BTN_DPAD_RIGHT, 2, NS2_BTN_U3_RIGHT, }, + { BTN_TL, 2, NS2_BTN_U3_L, }, + { BTN_TL2, 2, NS2_BTN_U3_ZL, }, + { BTN_SELECT, 1, NS2_BTN_U2_MINUS, }, + { BTN_THUMBL, 1, NS2_BTN_U2_LS, }, + { KEY_RECORD, 1, NS2_BTN_U2_CAPTURE, }, + { BTN_GRIPR, 2, NS2_BTN_U3_SL, }, + { BTN_GRIPR2, 2, NS2_BTN_U3_SR, }, + { BTN_GRIPL, 3, NS2_BTN_U4_GL, }, + { /* sentinel */ }, +}; + static const struct switch2_ctlr_button_mapping ns2_right_joycon_button_mappings[] = { { BTN_SOUTH, 0, NS2_BTNR_A, }, { BTN_EAST, 0, NS2_BTNR_B, }, @@ -3137,6 +3183,23 @@ static const struct switch2_ctlr_button_mapping ns2_right_joycon_button_mappings { /* sentinel */ }, }; +static const struct switch2_ctlr_button_mapping ns2_right_joycon_button_unified_mappings[] = { + { BTN_SOUTH, 0, NS2_BTN_U1_A, }, + { BTN_EAST, 0, NS2_BTN_U1_B, }, + { BTN_NORTH, 0, NS2_BTN_U1_X, }, + { BTN_WEST, 0, NS2_BTN_U1_Y, }, + { BTN_TR, 0, NS2_BTN_U1_R, }, + { BTN_TR2, 0, NS2_BTN_U1_ZR }, + { BTN_START, 1, NS2_BTN_U2_PLUS, }, + { BTN_THUMBR, 1, NS2_BTN_U2_RS, }, + { BTN_C, 1, NS2_BTN_U2_C, }, + { BTN_MODE, 1, NS2_BTN_U2_HOME, }, + { BTN_GRIPL2, 0, NS2_BTN_U1_SL, }, + { BTN_GRIPL, 0, NS2_BTN_U1_SR, }, + { BTN_GRIPR, 3, NS2_BTN_U4_GR, }, + { /* sentinel */ }, +}; + static const struct switch2_ctlr_button_mapping ns2_procon_mappings[] = { { BTN_SOUTH, 0, NS2_BTNR_A, }, { BTN_EAST, 0, NS2_BTNR_B, }, @@ -3158,6 +3221,27 @@ static const struct switch2_ctlr_button_mapping ns2_procon_mappings[] = { { /* sentinel */ }, }; +static const struct switch2_ctlr_button_mapping ns2_procon_unified_mappings[] = { + { BTN_SOUTH, 0, NS2_BTN_U1_A, }, + { BTN_EAST, 0, NS2_BTN_U1_B, }, + { BTN_NORTH, 0, NS2_BTN_U1_X, }, + { BTN_WEST, 0, NS2_BTN_U1_Y, }, + { BTN_TL, 2, NS2_BTN_U3_L, }, + { BTN_TR, 0, NS2_BTN_U1_R, }, + { BTN_TL2, 2, NS2_BTN_U3_ZL, }, + { BTN_TR2, 0, NS2_BTN_U1_ZR, }, + { BTN_SELECT, 1, NS2_BTN_U2_MINUS, }, + { BTN_START, 1, NS2_BTN_U2_PLUS, }, + { BTN_THUMBL, 1, NS2_BTN_U2_LS, }, + { BTN_THUMBR, 1, NS2_BTN_U2_RS, }, + { BTN_MODE, 1, NS2_BTN_U2_HOME }, + { KEY_RECORD, 1, NS2_BTN_U2_CAPTURE }, + { BTN_GRIPR, 3, NS2_BTN_U4_GR }, + { BTN_GRIPL, 3, NS2_BTN_U4_GL }, + { BTN_C, 1, NS2_BTN_U2_C }, + { /* sentinel */ }, +}; + static const struct switch2_ctlr_button_mapping ns2_gccon_mappings[] = { { BTN_SOUTH, 0, NS2_BTNR_A, }, { BTN_EAST, 0, NS2_BTNR_B, }, @@ -3175,6 +3259,23 @@ static const struct switch2_ctlr_button_mapping ns2_gccon_mappings[] = { { /* sentinel */ }, }; +static const struct switch2_ctlr_button_mapping ns2_gccon_unified_mappings[] = { + { BTN_SOUTH, 0, NS2_BTN_U1_A, }, + { BTN_EAST, 0, NS2_BTN_U1_B, }, + { BTN_NORTH, 0, NS2_BTN_U1_X, }, + { BTN_WEST, 0, NS2_BTN_U1_Y, }, + { BTN_TL2, 2, NS2_BTN_U3_L, }, + { BTN_TR2, 0, NS2_BTN_U1_R, }, + { BTN_TL, 2, NS2_BTN_U3_ZL }, + { BTN_TR, 0, NS2_BTN_U1_ZR }, + { BTN_SELECT, 1, NS2_BTN_U2_MINUS, }, + { BTN_START, 1, NS2_BTN_U2_PLUS, }, + { BTN_MODE, 1, NS2_BTN_U2_HOME }, + { KEY_RECORD, 1, NS2_BTN_U2_CAPTURE }, + { BTN_C, 1, NS2_BTN_U2_C }, + { /* sentinel */ }, +}; + static const uint8_t switch2_init_cmd_data[] = { /* * The last 6 bytes of this packet are the MAC address of @@ -3787,11 +3888,51 @@ static int switch2_event(struct hid_device *hdev, struct hid_report *report, uin switch (report->id) { case NS2_REPORT_UNIFIED: - /* - * TODO - * This won't be sent unless the report type gets changed via command - * 03-0A, but we should support it at some point regardless. - */ + if (size < 0x3f) + return -EINVAL; + + switch (ns2->ctlr_type) { + case NS2_CTLR_TYPE_JCL: + switch2_report_stick(input, &ns2->stick_calib[0], + ABS_X, false, ABS_Y, true, &raw_data[11]); + switch2_report_buttons(input, &raw_data[5], + ns2_left_joycon_button_unified_mappings); + break; + case NS2_CTLR_TYPE_JCR: + switch2_report_stick(input, &ns2->stick_calib[0], + ABS_X, false, ABS_Y, true, &raw_data[14]); + switch2_report_buttons(input, &raw_data[5], + ns2_right_joycon_button_unified_mappings); + break; + case NS2_CTLR_TYPE_GC: + input_report_abs(input, ABS_HAT0X, + !!(raw_data[7] & NS2_BTN_U3_RIGHT) - + !!(raw_data[7] & NS2_BTN_U3_LEFT)); + input_report_abs(input, ABS_HAT0Y, + !!(raw_data[7] & NS2_BTN_U3_DOWN) - + !!(raw_data[7] & NS2_BTN_U3_UP)); + switch2_report_buttons(input, &raw_data[5], ns2_gccon_unified_mappings); + switch2_report_stick(input, &ns2->stick_calib[0], + ABS_X, false, ABS_Y, true, &raw_data[11]); + switch2_report_stick(input, &ns2->stick_calib[1], + ABS_RX, false, ABS_RY, true, &raw_data[14]); + switch2_report_trigger(input, ns2->lt_zero, ABS_Z, raw_data[0x3d]); + switch2_report_trigger(input, ns2->rt_zero, ABS_RZ, raw_data[0x3e]); + break; + case NS2_CTLR_TYPE_PRO: + input_report_abs(input, ABS_HAT0X, + !!(raw_data[7] & NS2_BTN_U3_RIGHT) - + !!(raw_data[7] & NS2_BTN_U3_LEFT)); + input_report_abs(input, ABS_HAT0Y, + !!(raw_data[7] & NS2_BTN_U3_DOWN) - + !!(raw_data[7] & NS2_BTN_U3_UP)); + switch2_report_buttons(input, &raw_data[5], ns2_procon_unified_mappings); + switch2_report_stick(input, &ns2->stick_calib[0], + ABS_X, false, ABS_Y, true, &raw_data[11]); + switch2_report_stick(input, &ns2->stick_calib[1], + ABS_RX, false, ABS_RY, true, &raw_data[14]); + break; + } break; case NS2_REPORT_JCL: switch2_report_stick(input, &ns2->stick_calib[0], ABS_X, false, -- 2.54.0