From nobody Fri Sep 25 17:42:22 2026 Received: from mail-oo2-f42.google.com (mail-oo2-f42.google.com [74.125.231.170]) (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 299FC387346 for ; Thu, 10 Sep 2026 03:21:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789010491; cv=none; b=eVSDrM8lHl9U3pMRbr7WqB+2POh/Ro+V/QW06w0AS9anW0WEFt4X2Q7pv/axrD4Bhv4S05d6AR9DpGHaCSFpli0pvUDKTYCV5kW5Z9aSBksSQ2IYbwExdIMSKoRBcfm96GfhC8wTudnUVyiPNMNPh93WU41/0DaiAabwU6Vb1bU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789010491; c=relaxed/simple; bh=fUw52Q8KyFVDA140W88rVTkiznLG/tQsbaNZPy244T4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=KBKNMn4HdJLvehnM+qklVWWaglKIJz+9ZoloDZLdqYniExFlcxNB7gkpDQzjykyBhtcqFONUxJ1rGpy2tJhaelL2apbYze29d78UyMuho2sHWZTAX0IjQhthTve0U0ZEADF3hAR+bwKJV5YBGCGneajcVRumIqkRF1b3ZTat/ow= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=GA6qKRz+; arc=none smtp.client-ip=74.125.231.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="GA6qKRz+" Received: by mail-oo2-f42.google.com with SMTP id 006d021491bc7-6bf73b4fbedso7429eaf.2 for ; Wed, 09 Sep 2026 20:21:29 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1789010489; x=1789615289; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=Op8qEawLmjCmD3fm/ve6G52a6gnFB0iRG70Dt9TzKTY=; b=GA6qKRz+JSRfhOCMtQfN5atgBMlSjV20HOsOONkNtZ9oqA4isKBxidqJ8oN/qe5gPk h+9iQtHhEzPXonbCI9+67Epd7m6UCVuoQsTrLNqEVcJ9F3ep00tYdqhWQk4t0pD6fnpQ suoCknUB/qarOGBYhJsUWl6HKWfySgMnAvCiHYsjlgR69+3guZwuNJUV1adiLvXryx9S ApCXmDbtPgLXXBExpuPfIEqZhkt+6i0C/OZFtSe6I8q5woTJEQJGch/B4wdycA1ORP/T DcACPyeFfe5VfYwooxbij4MT0Y0BppwQMx6PPV9ytSJYofI6ONVewJNon9IzYQV20UI/ 6Zag== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1789010489; x=1789615289; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=Op8qEawLmjCmD3fm/ve6G52a6gnFB0iRG70Dt9TzKTY=; b=sAdEJwxUKAJvWXPL4nI88FZu73LcrVkDhtxfxwiRJC3RcWOxxDEcbN1FMuTnHqHNZr jnTMGPT23ug7zHQmkLiszHTG801AldztyHl3KL1xuwgJLdjsqB7SXmi4exl9V59HNcbK DxUCdjHvcQXBoPzU9bW69LF75gLopzGhoQlRAQbuzDphUyMIRK2AMc0Z0gmzaor73Ggp onQkKZftCNX1+4caJsKsiqdn70gT2TSTnDWUQQOToMcD9WAMWl3o16dPM/IpdFZ/CX+e I7EodMn1PLEcoGXFRyDO32FmtjDI+YBzhRJ+eWjpMVQkLBQUWUwVOS98WXPwS5FvY6Je F1qg== X-Forwarded-Encrypted: i=1; AKwUvBzBFNg1x/i9Ag8lHpqMIFzeoK/U+AoUp9mux7b5idQpLxQDRbwk0T1hb/JZY4O4vAVfrrM5r5D0NTLO31c=@vger.kernel.org X-Gm-Message-State: AFuF++nfOom9PZQdA7P2tldgR9ro9v6Wmx8lCYL8uO15GSQHzxfE5QEQ IZHrelh2i/DwOPXR5t3NLBR1iyy9HGhHKIRimbBPDwGvwUUbWJyYnQGz X-Gm-Gg: AYBFou2R5EF1Pe4q8+VqSWuaC1hkchl2RvJOEEzvbpoevzZSQP6TizYcTUoEq6zaLYE fOmadtV1DW8iEYb2/fImPvUyrs66kwjDi7i02LDcuvHptN045yPgcrhMPWNyC6ySdV6bewGuTeU 1AsHaV4Ry5iLF+KlxeDU7R0FDoKsJSiQ3axIpX7k/GL8QRyyDhO/d8hVKVlXMJttBP8b4Qc2pmo yAhX0DYYN9IKC/tjwCdlX1j3tXPdMQ0UJwXiHcAmkKFkkhWVDdjyehAYRpCarFfxNKX9fhqtsIm GM1j8FDfbZVANRUYHJ9c9GpWUQ1Mk7XANrg9kUJq9vkwmw6dIT5qRZ/iZDvJFmpWD+VSvTB4mhE z2T7IATAZgVWlbtz+tLgxZkgKHvH6xcT4Ic+a4R981+nQyrA/jc+gq8UWL02Rp8rfakCQBqa76H Lm8cA1KIWINQNzhPkb8bs4DL3PP148mKoQx/u0Q3Pg3BHtBkoZkvdUrXYQKIRNkyZy4rN/wCfbQ Pdz66pZBtH1x3gQmZnAIF4wYeJykS7JB5jvpYMMGr6fwdiqe75FSpxZn5kfJoLkYw== X-Received: by 2002:a05:6830:368b:b0:7e9:d593:896f with SMTP id 46e09a7af769-7fb79dd122bmr20984742a34.1.1789010488832; Wed, 09 Sep 2026 20:21:28 -0700 (PDT) Received: from localhost ([76.187.192.44]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-7f9f6da9a49sm19758315a34.13.2026.09.09.20.21.28 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Wed, 09 Sep 2026 20:21:28 -0700 (PDT) From: Andrei Aldea To: Jiri Kosina , Benjamin Tissoires , "Derek J. Clark" Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 01/15] HID: hid-oxp: fix default M1 and M2 key mappings Date: Wed, 9 Sep 2026 22:21:01 -0500 Message-ID: <20260910032115.28669-2-andrei1998@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260910032115.28669-1-andrei1998@gmail.com> References: <20260910032115.28669-1-andrei1998@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The default button map intends to assign F15 and F16 to M1 and M2, but the selected mapping table indexes resolve to F16 and F17. Use indexes 47 and 48 so the programmed usages match the existing comments. Assisted-by: LLM Fixes: e4c850a6e750 ("HID: hid-oxp: Add Button Mapping Interface") Reviewed-by: Derek J. Clark Signed-off-by: Andrei Aldea --- drivers/hid/hid-oxp.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-oxp.c b/drivers/hid/hid-oxp.c index d2ded6b..3f2be80 100644 --- a/drivers/hid/hid-oxp.c +++ b/drivers/hid/hid-oxp.c @@ -607,9 +607,9 @@ static void oxp_set_defaults_bmap_2(struct oxp_bmap_pag= e_2 *bmap) bmap->btn_dright.button_idx =3D BUTTON_DRIGHT; bmap->btn_dright.mapping_idx =3D 15; bmap->btn_m1.button_idx =3D BUTTON_M1; - bmap->btn_m1.mapping_idx =3D 48; /* KEY_F15 */ + bmap->btn_m1.mapping_idx =3D 47; /* KEY_F15 */ bmap->btn_m2.button_idx =3D BUTTON_M2; - bmap->btn_m2.mapping_idx =3D 49; /* KEY_F16 */ + bmap->btn_m2.mapping_idx =3D 48; /* KEY_F16 */ } =20 static void oxp_page_fill_data(char *buf, const struct oxp_button_idx *but= tons, From nobody Fri Sep 25 17:42:22 2026 Received: from mail-oo2-f43.google.com (mail-oo2-f43.google.com [74.125.231.171]) (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 B7552388371 for ; Thu, 10 Sep 2026 03:21:30 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789010492; cv=none; b=Qj2Tl33VAdphBMs/nw/vdyFbnV5rFM8N+VnV4vYOB99iyb9EgOUIeVB9YFFunXLy4zfaTbcX4cA/sLiRmzrnmKW7r2PeJ4hDSlMIeBqyALxuzfU7OpztT9AJVX0WepBK703KIkeq0mVF/TcHXfXw6tRpBoxDpKVr6Cd6drwOWSQ= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789010492; c=relaxed/simple; bh=RAnQLpzPku7/sBiN9IYfkstPCRnhG64DyTCe5z9u7y8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=u4t9FgphmsUSkC0UmSkwnMVRzgWGMQpmxGWaxoqRJKt7xa+xatiB5nz6J4ipahYODu/8YSbgTgLQ7apRgTnbz3HyH/tIiu6SrCP49v6TAnFgUvht2YoJvAeNst9ZPReaVPwv8KwCU/2XHZfUmPYRqVO5Ag4PUFJ6Jy8087pa8tA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=GeZITH3U; arc=none smtp.client-ip=74.125.231.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="GeZITH3U" Received: by mail-oo2-f43.google.com with SMTP id 46e09a7af769-7f4bdf26365so367304a34.3 for ; Wed, 09 Sep 2026 20:21:30 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1789010490; x=1789615290; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=uB3vWkzSWUGhUPGiwsOLzELGJs5tqcYbrEX4UJLtth4=; b=GeZITH3UzsOViFtkF6z9QYo26zh0ijQHzdUlyFucjrG/2B4z4X41RZyFl+LQNDvvc3 wSb9v3TnoR5kfE/Itgu8KtyMjrtESObaGlsvjRnOYQX3yWFQBv3SjEJK33EFDgEIwFUk yIV2aG3ttc6+zqlgkm4XSviEL/zpGZEY724u2v6yzagXIA//L9hPlqKJ8eDPly1vJvVy 0c5jXQCzxq7K4vQKkmKcNz5B5FZsja64eMwI8UReOPj+fa+fWGNDJOau8FrCTLA3zJ73 ebPau1cHtliud5gAay/TQ+TOah4E3JkspJ1qwb7/TQKwquFdInzwb3QMxt///VesmHxv rJVw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1789010490; x=1789615290; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=uB3vWkzSWUGhUPGiwsOLzELGJs5tqcYbrEX4UJLtth4=; b=jivPZKAvPs3Poz/1zTDnMMabpjiB+6W4ST0STAnBdQxUpbf3EY6P3RT6EsCOc3gzBE 1E0U+RbuDG6b0XVNsjHe6Z7jKdAv3FpRCQuKYXv4nGrQsWfc8J0nZZ4DacHkcJSMSqhl FQIDYKfSzWBs7QRub+7iPS5+e+LySN6Xp3oDqkNdmkXoDxSuy+WksdR5C/oImsKAHH9B nz2CNajcN8vjopeuzq5DKk6cuvFlLe4KHkM6Hu2czi05NTIp7FT+p536Ts+i3XiZOvl0 eYnwy8mFmQ8qQXH+LOye5fCt2ECUCT0k/XQ6CtaAWlJDKbQkU1pYCQWJGQZaQmu8Dtyd WeYg== X-Forwarded-Encrypted: i=1; AKwUvBxpJacbIi/ox16cgbGmjxSrmWC75Oei1HngxGkiOl0uqK/avALWOukeF8kKKFolXRwjQqUMORKcGc8jGGQ=@vger.kernel.org X-Gm-Message-State: AFuF++naYkqJfnYvLXSwuXxSzucuxaHOz7ZPbyuhNM6Ar2XmW5VZ+ejJ fd1B5WrQ6cM+TsaLkuU4SqPtAxzgoQn6FwXjey4lpz/yiQFRax4Wex2D X-Gm-Gg: AYBFou0hZOwsCom+UeX1o5GPYIo8GJjiUxFWgnGBh32c+4VeKd95sjwtEPTpW2HexDY QdqbdUKNcEBRpxWZoKgD7ufsGJ7aKmAgNSwqvycc/AWk+tev0CqRIDsapUjrFRwbOv/hqoLdVAe cNc+A6NUI7yTo9nhJPgd2FoYYwOdNrAbjlbtiqpmnNfUj1qlAHeajFdH5HBANJNhejh7uttiUlq cVXLy4xPvgPZn4eoKFRfBXSfHRYh24wGkoNSgiHyf49XXk4De1+JqVGnyjrnlSMb/oKe9GcbFCA nmF5anBwSo4zTTSV3A8U4LYzhV7a8V38I/Jfr3Sn+m+84JWLYfMribu0sdxomee/9ok/eKvgPzo Z/I2xsUCqSIow+rw27M8YjT6H3zn7uonB/gQ6mNqGwUUuOG9nEsaksDxrRwH2CEO/WRmeku4nZU u7kG6+9Hrex/rBcjWj/JUseS7Oln09lVRtOjyl/tGguKCezTj6BGClsCc13MO3coFxhWxFIV3EO dAa2MtcAaU97jp74GPbHhX1+zsvOkSQN3k1UuhHZczl09n3wcmvYkwufQfWKGp8OQzE X-Received: by 2002:a05:6830:83aa:b0:7e3:f809:7984 with SMTP id 46e09a7af769-8027109f9aamr2270980a34.1.1789010489743; Wed, 09 Sep 2026 20:21:29 -0700 (PDT) Received: from localhost ([76.187.192.44]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-7f9f6da9a49sm19758315a34.13.2026.09.09.20.21.28 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Wed, 09 Sep 2026 20:21:29 -0700 (PDT) From: Andrei Aldea To: Jiri Kosina , Benjamin Tissoires , "Derek J. Clark" Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Lee Jones , Pavel Machek , linux-leds@vger.kernel.org Subject: [PATCH 02/15] HID: hid-oxp: validate input report lengths before decoding Date: Wed, 9 Sep 2026 22:21:02 -0500 Message-ID: <20260910032115.28669-3-andrei1998@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260910032115.28669-1-andrei1998@gmail.com> References: <20260910032115.28669-1-andrei1998@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Check the short Gen2 status header before reading its command, and require a complete RGB status report before reading either generation's fields. Use the supplied input length for the debug dump as well, so a short report cannot cause an out-of-bounds read while logging. Fixes: 84910c459d65 ("HID: hid-oxp: Add OneXPlayer configuration driver") Assisted-by: LLM Reviewed-by: Derek J. Clark Signed-off-by: Andrei Aldea Tested-by: Derek J. Clark --- drivers/hid/hid-oxp.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-oxp.c b/drivers/hid/hid-oxp.c index 3f2be80..0c895bd 100644 --- a/drivers/hid/hid-oxp.c +++ b/drivers/hid/hid-oxp.c @@ -23,6 +23,7 @@ #include "hid-ids.h" =20 #define OXP_PACKET_SIZE 64 +#define OXP_STATUS_HEADER_SIZE 6 =20 #define GEN1_MESSAGE_ID 0xff #define GEN2_MESSAGE_ID 0x3f @@ -318,6 +319,9 @@ static int oxp_hid_raw_event_gen_1(struct hid_device *h= dev, struct led_classdev_mc *led_mc =3D drvdata.led_mc; struct oxp_gen_1_rgb_report *rgb_rep; =20 + if (size < sizeof(*rgb_rep)) + return 0; + if (data[1] !=3D OXP_FID_GEN1_RGB_REPLY) return 0; =20 @@ -391,6 +395,9 @@ static int oxp_hid_raw_event_gen_2(struct hid_device *h= dev, struct led_classdev_mc *led_mc =3D drvdata.led_mc; struct oxp_gen_2_rgb_report *rgb_rep; =20 + if (size < OXP_STATUS_HEADER_SIZE) + return 0; + if (data[0] !=3D OXP_FID_GEN2_STATUS_EVENT) return 0; =20 @@ -404,6 +411,8 @@ static int oxp_hid_raw_event_gen_2(struct hid_device *h= dev, =20 if (data[3] !=3D OXP_GET_PROPERTY) return 0; + if (size < sizeof(*rgb_rep)) + return 0; =20 rgb_rep =3D (struct oxp_gen_2_rgb_report *)data; /* Ensure we save monocolor as the list value */ @@ -435,7 +444,7 @@ static int oxp_hid_raw_event(struct hid_device *hdev, s= truct hid_report *report, { u16 up =3D get_usage_page(hdev); =20 - dev_dbg(&hdev->dev, "raw event data: [%*ph]\n", OXP_PACKET_SIZE, data); + dev_dbg(&hdev->dev, "raw event data: [%*ph]\n", size, data); =20 switch (up) { case GEN1_USAGE_PAGE: From nobody Fri Sep 25 17:42:22 2026 Received: from mail-oo2-f42.google.com (mail-oo2-f42.google.com [74.125.231.170]) (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 B76D13905F0 for ; Thu, 10 Sep 2026 03:21:31 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789010493; cv=none; b=Fxz2LwUfeW0W2mGYrgOqrJsp2K4PNR/7sZE9x6gEYx4V63Y/K+m6OUIY3s2p5mrYCR1HQnbgVjgyhWl5LYwAeCyYS2xYONMsWLkkl3tJz+8Aeth4KrfzO8xscKyXLwNgKHWMDWd/bkl94KjXcNHaDSaqDLwfJf/7kKTV2huqJII= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789010493; c=relaxed/simple; bh=jHHrb1gkKCrQofVQkjlJcoMNCA+FXIvxVOvLoWJQF5s=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=X2MDTdrQoKbyKawlhZT/+hnGzNOpg0DAdR03vGQQ9DS9Z75Ni9PhCNl2R9jjDAS0j/AQPBMoMJYfc4JpDvMqyvDKy+vSyApjwyJdPpsYKHI65z7wBMnJVFPxKU2verbjPLsVfxluJM2ocu25mT1DAvcwZca5qRgNfkhFlNbD1mY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=tAlfqRO+; arc=none smtp.client-ip=74.125.231.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="tAlfqRO+" Received: by mail-oo2-f42.google.com with SMTP id 46e09a7af769-7f4bdf4f0e7so686405a34.3 for ; Wed, 09 Sep 2026 20:21:31 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1789010491; x=1789615291; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=CrGYYRXsocnHt4R7I9PwAhwsbtA+DaKDKrj9nTvfp+g=; b=tAlfqRO+dj20/CslGq/8CS4REHc87cHhZWBpneqL8yMS2KHPSlExi9EXp5TlN1rrRS PX2OyQ1DtohPQQRFc67f0x0miEKxBTChmN2ULNPoLG+UR6mtijVXO/Va6ldkadtre3u7 oMRlBYfJU943iYcE4k9P3m+E2EFUhvBm7Y23tLy36EPqHRY9bPlOizI+APFjlzqhqbYn s6KDyttuSzqf2WTx38JEeNET3x/CNTWy3FSl/5K+UyQ2+GQpJqGlOuNToXSzPLRYhX71 jCp95ltzfAc1quD8WprcCOWNBZ06nDx/xd0PnTndPeemFPlMUngikvyLvvCtAGfl37lU tMAQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1789010491; x=1789615291; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=CrGYYRXsocnHt4R7I9PwAhwsbtA+DaKDKrj9nTvfp+g=; b=dHPU/WZv7mcNm85+fuue0q/TqAjIW6uZ6PwOrSGyUPVW/LZDZRBT/wfMn4IdxyisEa AZksDA/lsjs9PXVR7As+LHNlCbma22P1jYnDSzUNnXz3Fb3lrWIXMquVdS35ePBx36gx Lw8hRNFqH4YpWBMHNaZZ3+Ne5q+gCquBAU7AYq65H4fq2uXn+unQPhMmUo3B7mfB5LQz sCqN9/6UTdT8Ac1l7pDq0xCvd97hdcTI5hNKrRj2do2TLLN+R1w4ZAhMWNUNV614VQdS 769WghC301UIF8OU8FYh8gvk2fda9PxIZHwBubkbJ+t/EV/L/uNj79ROwUMo2OQCyKXV NqOA== X-Forwarded-Encrypted: i=1; AKwUvBxgMpLRRN35bTBCXph7TSe/82mCyaKtx2lMJb+RmiB8UKOqvE51VTA6Rcl8JTtuJDj82sJ2jqccEDcfHN4=@vger.kernel.org X-Gm-Message-State: AFuF++mux9yuV8VADFegUSd/GcPKGA5T8ux28vdbvBehzkNUJEGFDGHJ WsW1lB2gYaDsWJ+6EYaomNjkyLebJ5GOCYTCJ/Ee1D+WbJDEXgIbKRB6 X-Gm-Gg: AYBFou15W32JygVR9thOFpvUSMu9iWqQJnlfLQV3/4sPkzCuvu2wJihu9gINcJajKf2 n2nu+36cbMK0ioXHVCO9APo6E7UmDg7WomLGmibqO2/NL78K1QQYFIMRAFcDmqQYqdwCd9lET8Y Ns8/dC6buX05RZ/Zm6835WRPuIlQKMoPe0h0JevOd37HfqpESSvqhPiAVCJiJPzvwgjDFiej2UZ +M/jvBj7f6lit/DjPQV8Ofv811nXZkvel0QX5B36GcEipzcd2grdrJOVXCcTJj+js2HcOFGZ1K+ TUM9Ss6SFqIjMxDX9LTig08LmHLC1kCqhGm0x2uKLptJ51o4h+VSkpp0db5lsuIBeLK/AjgVFlI w3y5mTR9z8QHI7rbsNoPFdXgdDmU0lWX/0DWR04vfVvpIGgM0hye/BwLVPy6PG3tkaCqgXQfAlN P0orudno+EmyCyHp/sl2vIUdEgNdjqnXaz5q/Pain4mEw583FUbUrrhF4qSiPgzZlAGyRtwN9um 21FXUq21neNEtfomXesRmkCGxP5ODusZFK/2Uj3USxOUWs+XaRTVoc= X-Received: by 2002:a05:6830:3495:b0:801:b3e8:25ab with SMTP id 46e09a7af769-801b3e8a6c8mr4013717a34.2.1789010490678; Wed, 09 Sep 2026 20:21:30 -0700 (PDT) Received: from localhost ([76.187.192.44]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-7f9f6da9a49sm19758315a34.13.2026.09.09.20.21.29 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Wed, 09 Sep 2026 20:21:30 -0700 (PDT) From: Andrei Aldea To: Jiri Kosina , Benjamin Tissoires , "Derek J. Clark" Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Lee Jones , Pavel Machek , linux-leds@vger.kernel.org Subject: [PATCH 03/15] HID: hid-oxp: retain fractional brightness when reading RGB status Date: Wed, 9 Sep 2026 22:21:03 -0500 Message-ID: <20260910032115.28669-4-andrei1998@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260910032115.28669-1-andrei1998@gmail.com> References: <20260910032115.28669-1-andrei1998@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Multiply the hardware brightness level by the LED brightness range before dividing by four. Dividing first reported every intermediate hardware level as zero instead of 25, 50 or 75 percent. Fixes: 84910c459d65 ("HID: hid-oxp: Add OneXPlayer configuration driver") Assisted-by: LLM Reviewed-by: Derek J. Clark Signed-off-by: Andrei Aldea Tested-by: Derek J. Clark --- drivers/hid/hid-oxp.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/drivers/hid/hid-oxp.c b/drivers/hid/hid-oxp.c index 0c895bd..86a559c 100644 --- a/drivers/hid/hid-oxp.c +++ b/drivers/hid/hid-oxp.c @@ -334,8 +334,8 @@ static int oxp_hid_raw_event_gen_1(struct hid_device *h= dev, drvdata.rgb_en =3D rgb_rep->enabled =3D=3D 0 ? OXP_FEAT_DISABLED : OXP_FEAT_ENABLED; drvdata.rgb_brightness =3D rgb_rep->brightness; - led_mc->led_cdev.brightness =3D rgb_rep->brightness / 4 * - led_mc->led_cdev.max_brightness; + led_mc->led_cdev.brightness =3D rgb_rep->brightness * + led_mc->led_cdev.max_brightness / 4; /* If monocolor had less than 100% brightness on the previous boot, * there will be no reliable way to determine the real intensity. * Since intensity scaling is used with a hardware brightness set at max, @@ -423,8 +423,8 @@ static int oxp_hid_raw_event_gen_2(struct hid_device *h= dev, drvdata.rgb_en =3D rgb_rep->enabled =3D=3D 0 ? OXP_FEAT_DISABLED : OXP_FEAT_ENABLED; drvdata.rgb_brightness =3D rgb_rep->brightness; - led_mc->led_cdev.brightness =3D rgb_rep->brightness / 4 * - led_mc->led_cdev.max_brightness; + led_mc->led_cdev.brightness =3D rgb_rep->brightness * + led_mc->led_cdev.max_brightness / 4; /* If monocolor had less than 100% brightness on the previous boot, * there will be no reliable way to determine the real intensity. * Since intensity scaling is used with a hardware brightness set at max, From nobody Fri Sep 25 17:42:22 2026 Received: from mail-oa2-f12.google.com (mail-oa2-f12.google.com [74.125.231.76]) (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 A0036391E7A for ; Thu, 10 Sep 2026 03:21:32 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.76 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789010494; cv=none; b=PzO0LWBMbQ/eY8o28Joe8VmCpsimI57kG20qCxpg5vnOjhyGsK0/S3vJTPUebI542OMEsP2ArVPiGZY8uswr6Zd3bkItGZXA7OYRBfbN93FWifWsQjhwYgzXi6hhMrTVkoWuDe7zwt5/sXhQVUO2ISZ7vCdl5TPt1qqJzxGfWyo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789010494; c=relaxed/simple; bh=8jNRGrzAMfC74cF7QqblcNHcIj2RnLHXXe610hq/Vjw=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=IJXauTGBmBFQ0XrkcrnZhO4A56DhQsq8rhyJThk3OQoUzsrat1Z2fgz44B/ZHo/N7ypnFwCqJT3dOjxfHEScDx6IizJ5JQHHDCNyHrFqoeVsuoPjLGr4HoDzE2KK6J318NhTKOO7nDNWBWfLJVz27YDwFgB5hkW+SnBCVmHjo/E= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=sUgJu/pa; arc=none smtp.client-ip=74.125.231.76 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="sUgJu/pa" Received: by mail-oa2-f12.google.com with SMTP id 586e51a60fabf-46af36be149so1557533fac.3 for ; Wed, 09 Sep 2026 20:21:32 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1789010491; x=1789615291; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=NSfuMa1PaK7LxfGHLbiVRQmOmAt7jKjptTGVArTkaVc=; b=sUgJu/pa74fYdEWss+vwiyBP7BlMRkYj4+FdiZvmMBt5IY8z3hueuEWyU+xmevdXQU AkI2yGuxd5iBaC5x0B0NLJk6sMMCt94EpuXYPZ39/HtdDNruzwE1x27Ju3DAO3S7qpNB ewY4Gb1FdTKFR4ISANInAS3sDP/LJtgWrpXUUrqW2ErnN+A1h0p4dfRGs5+gX0glqZ6A eknYnDfE244YEpRAWTX3By1fQZr5NJqS998zYksccEnc+05IEAng+BWNZVKiGoDFQYf4 UsRK8nOUb50gjGyt7f7WkGHetAabx0w48aFZ5SeU2LjdygYjDvBmob+dajT2hKEfuHha 3Gog== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1789010491; x=1789615291; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=NSfuMa1PaK7LxfGHLbiVRQmOmAt7jKjptTGVArTkaVc=; b=mBEk88tjohNji2jD3QS0ppLogyknJ2scCGp5CvmnYWxasft16QeDY25a0V7OfNwNZF IwBUI5M2V/Wl0yWYMEmdP0/DlonytVga334bqsrniZS8AUJl5AZn5zzGjW1v7fNM1vbM iZ+JcmfW7Ovwd3NY4rFOhYbiXC8XoDu2zcKESVl1PmG7Awekozwr3hgZcKvm+tg75iZ9 UYlvQxwHCYOkBRHaHSblwT47er5ggZyMk+MDpuHZDs6T+82qEZHyMZJGbDHuL1gmAeUd DespGPUW/afBhDcoP+JowzCqJFhpvKcEMt9EvDuSdupjLVgGFN1fFvjDsqYv+rfeF/jI 8xkw== X-Forwarded-Encrypted: i=1; AKwUvBzc0sxTOuhJUhVrUnJ1vh3F4i8ocnPTJ0KUnuM0LwLLe08A6ml4GVYtuZ0ImZ1jo7f2VLL3uL+Bv5EvQjA=@vger.kernel.org X-Gm-Message-State: AFuF++nDamdKgohtyiP9lTJNyd30or9231a6cA0J7f1e1PAImh3IH1sD lA7sqHFbeHJSSByTGPM02LxzB5kFjtn+ymQSAuOZ8rkeCLR7y05PPY2g4EEbGw/zeU+0ylPp X-Gm-Gg: AYBFou1hkIFQvSZGYX8RfjJ2zXNZi85MLPawueuTvGeL2JuMJ1YdLzK5C+g5BSi6w// tG9MuDc0PCZiBJFkiv1h3eXIE8wkF+pR3KNotIT7Eu6A4OOuqWYCSTwQsarTXW5qOIzditx4Y/U P6DKZFbmKBMCiGxlvLODcyogx6h/Uvge9r11eSY24xmup8xsasTWc4o1gXf7RYPzPLsauwLT+be KZuiKS3Zp5555bQHYDlZVZcqckp+YRy8lNM1sqdfE+KDrm2lR2b3Eh/HtYiqzJ4f6N4uRQFfctC U582T9hfDQ/47BEFpX2H4xrSLpXZKjarp7QOsyPsbreuDjHS0v7HjOFS+sO9oGnGQQlDFGi+33V gWI64r/Ui+vuI5/roaqM+AHssNEDVyK17HzTJoNkNsVNLU8gxKk+h6NG4LdOnsm7I7VZd+z59Wc v8zwTNN9XFn2U0P/zWKRsZ73f6eeEhbHO+dOnhpnpana5ucu1nfQ6NItT30VfywlkdgE9TVbwk2 xRNe51/Qp+09mLVIi3Pqqzhqr2e9dxw+gVYMC+B/0KH7XvGRDvNNDA= X-Received: by 2002:a05:6830:67d2:b0:7e6:6ced:992f with SMTP id 46e09a7af769-7fb7bba55e3mr17226582a34.2.1789010491566; Wed, 09 Sep 2026 20:21:31 -0700 (PDT) Received: from localhost ([76.187.192.44]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-7f9f6da9a49sm19758315a34.13.2026.09.09.20.21.30 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Wed, 09 Sep 2026 20:21:31 -0700 (PDT) From: Andrei Aldea To: Jiri Kosina , Benjamin Tissoires , "Derek J. Clark" Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Lee Jones , Pavel Machek , linux-leds@vger.kernel.org Subject: [PATCH 04/15] HID: hid-oxp: reject invalid Gen2 RGB status values Date: Wed, 9 Sep 2026 22:21:04 -0500 Message-ID: <20260910032115.28669-5-andrei1998@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260910032115.28669-1-andrei1998@gmail.com> References: <20260910032115.28669-1-andrei1998@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Do not replace the cached RGB state with a Gen2 status report whose enable, speed or hardware brightness fields are outside the supported ranges. Fixes: 252c4bf1d931 ("HID: hid-oxp: Add Second Generation RGB Control") Assisted-by: LLM Reviewed-by: Derek J. Clark Signed-off-by: Andrei Aldea --- drivers/hid/hid-oxp.c | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/drivers/hid/hid-oxp.c b/drivers/hid/hid-oxp.c index 86a559c..c3cfa95 100644 --- a/drivers/hid/hid-oxp.c +++ b/drivers/hid/hid-oxp.c @@ -415,6 +415,10 @@ static int oxp_hid_raw_event_gen_2(struct hid_device *= hdev, return 0; =20 rgb_rep =3D (struct oxp_gen_2_rgb_report *)data; + if (rgb_rep->enabled > OXP_FEAT_ENABLED || rgb_rep->speed > 9 || + rgb_rep->brightness > 4) + return 0; + /* Ensure we save monocolor as the list value */ drvdata.rgb_effect =3D rgb_rep->effect =3D=3D OXP_EFFECT_MONO_TRUE ? OXP_EFFECT_MONO_LIST : From nobody Fri Sep 25 17:42:22 2026 Received: from mail-oo2-f42.google.com (mail-oo2-f42.google.com [74.125.231.170]) (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 A3D1E395AC6 for ; Thu, 10 Sep 2026 03:21:33 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789010495; cv=none; b=Z3fXWhnbULjA73xy0wH4ZyYwtd6+rJJFHZZVHfI58jU3iO/tM+9aSCeQ+1lFdLJlJ8bFsiioPUajrU8a3Ax9ik5UD0f6W+xuthZuLLC3Gpw1GUw1l/sYZ4jOC10IQ42pa+zPNeCyYX17KbhyJuTC7s+FNmB/JeOS0kjvCJ4x9y0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789010495; c=relaxed/simple; bh=gHUcDvNC7r65WiaZb3xBsRLpqSj1tHK4me1lBYfrO18=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=OKwsznH4u/HzqQTZyskCc7NuQG9tundzW2GeW4Nujude1exVVfzCuzCLhbYOaiwmxtDs0gMZmL1QP5KCLI9IQ3/IyV7o6N40FS0nXlmT4YRCuLIBQzJLlewmJwPhcHUT8y8ol5m7zZ7v7RmgjUBa0mNY8Is0nHGnHyLub/l5zuI= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=O7S6RxnA; arc=none smtp.client-ip=74.125.231.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="O7S6RxnA" Received: by mail-oo2-f42.google.com with SMTP id 46e09a7af769-7f4ee37dd94so218812a34.2 for ; Wed, 09 Sep 2026 20:21:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1789010492; x=1789615292; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=DoyJCop5B+D/2yEHeRAP7f3j2RNXYDtwYezKGq7KmzA=; b=O7S6RxnAkSJmJIUGLQ6+XDK821tlpbmzIaJpYgU4VskYHBSRgPm6p5yA4C4OiggeYS VEKb2nhbhgbRVgXKPrBWPYSkfmaw1ZLcMDp51VJ1oR4g/gMA/acKnEH+dbomu4Se6Tba wRWDABkhCfBNhVIVq7K2Ac6mwvW07ewDTI7KZfSoelaKC8+i8iSPCKCc0cUE4ZTQFzpy P+az9K0y4ZpIfj3+cmX2Vrwx33QRg6MsbyJVe/dIzLqaBaee3cyLxPD70wbPPKVTr0+C hOGYzrUk9xg2rE7sXrvVzjPluVzDZGzMteP041W5dR/joYglPmBduAJ5R8l6ylsKtXNT +1Gw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1789010492; x=1789615292; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=DoyJCop5B+D/2yEHeRAP7f3j2RNXYDtwYezKGq7KmzA=; b=i4IShJEJo1qQXIZAIezfJ+e+CyKFW4UFpPSVJ7oFK9/wM2tGUF00y3Qu4pnulhcbvU TE23WHL/J+xTnUn4yCsugsGsGm3MZiirp4HMmySQXrj6Zn0lvXpbiDp/8S/faXRmfIqJ SN8KfMd+8quNhY62ybN3/Sza9yDvNDQKJ0/wK+HZlS8mEEq/L4vQ4VwKHPMXo+QDlLuj k6gKM56O93X3qlV61n2nxvDp4pQ65YQL1LoTrI5vZ1E9QPgboYa0puIw3d031S53FqrD xn7bRUl2g4hXsdR5dF3wiYTN9ob+fJOY8L9xMpi7RWCTvxKhgaahlnFhgG6ElWMEwVWe zqBg== X-Forwarded-Encrypted: i=1; AKwUvBykiK93VpXp42Z0IhyUVsr4j8udAt8dpWWITUxsU9MSlzDL/iOl/7TczG6YKMVV69IYdmXYdhwFmXQmH0I=@vger.kernel.org X-Gm-Message-State: AFuF++kFCE7ZGtc62DL85MgsYeTbwzSN+zMNmtmAm7WVEq2v1xPyIgvD EnlS4DnMJTCxurlsLR31hyN9G3bV2iLN6rZ496gcW+WDeeiSH5HIaX5A X-Gm-Gg: AYBFou14NPImkjQdUWIlgBzE/V1fZEaFqEVAQ/R3wYFCsEOSN7Ghs31H+TppKVdVDP0 pPO8uDz7GdCAW7fg36IzurnVNwyV0+A3fgnLBkmjDiLgkzUAscWylRjjHf24V+UpwClK77nSioH jc+z61kkiXN41lTqkKsG4IzD9ekLNDaRDZFPsIbKqmHTpNv2Rmsg+wO95ER+dr+1RSTnbc0gO+o vMpPu75IKMjWFJu1IBgKEriPDtNliNA0qTfdIqP/iZdfkco4nT/WXAMQVHQp/tXVZOW3+C1tKw2 KzlnMFy1N/0PtsZRMbgG+MPdb+PVPGvyffNMOd+EDR8UoYFVlPvsHXPeeMkhu7dUZ8C3fLmHzxW 6vRIN0iywN/BCJX/KYx6yFNJyy0ky4umjZEMAbhsntxIZBLP9fkmXMbtSK11R6j8NmxX6iHF+sF 3n1ey5qIKEZdDVZht6Pwt10voKdlGEkbe39h5V+45VuLettc6R5UX2xxyM5XOFjXBH6RuAHT2V1 K+3vK/UXKNAUSKxryxgKpyaF3JnTas9aiNc374FnGm4QExM9U5CqmY= X-Received: by 2002:a05:6830:4987:b0:7f3:fdfa:b9a6 with SMTP id 46e09a7af769-7fb7c291268mr18073886a34.3.1789010492486; Wed, 09 Sep 2026 20:21:32 -0700 (PDT) Received: from localhost ([76.187.192.44]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-7f9f6da9a49sm19758315a34.13.2026.09.09.20.21.31 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Wed, 09 Sep 2026 20:21:32 -0700 (PDT) From: Andrei Aldea To: Jiri Kosina , Benjamin Tissoires , "Derek J. Clark" Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Lee Jones , Pavel Machek , linux-leds@vger.kernel.org Subject: [PATCH 05/15] HID: hid-oxp: fix multicolor LED intensity scaling Date: Wed, 9 Sep 2026 22:21:05 -0500 Message-ID: <20260910032115.28669-6-andrei1998@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260910032115.28669-1-andrei1998@gmail.com> References: <20260910032115.28669-1-andrei1998@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The controller accepts eight-bit RGB components, but the sub-LED channel maximum currently inherits the 0-100 overall brightness range. This prevents userspace from selecting channel intensities above 100. Set each channel maximum to 255 and use the multicolor LED core helper to scale components with the requested brightness. This also replaces the private truncating calculation with the LED core's rounded calculation. Keep the existing Gen1 and Gen2 color packet layouts unchanged. Fixes: 84910c459d65 ("HID: hid-oxp: Add OneXPlayer configuration driver") Assisted-by: LLM Reviewed-by: Derek J. Clark Signed-off-by: Andrei Aldea Tested-by: Derek J. Clark --- drivers/hid/hid-oxp.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/drivers/hid/hid-oxp.c b/drivers/hid/hid-oxp.c index c3cfa95..bee1317 100644 --- a/drivers/hid/hid-oxp.c +++ b/drivers/hid/hid-oxp.c @@ -1088,7 +1088,6 @@ static ssize_t oxp_rgb_status_show(void) =20 static int oxp_rgb_color_set(void) { - u8 max_br =3D drvdata.led_mc->led_cdev.max_brightness; u8 br =3D drvdata.led_mc->led_cdev.brightness; u16 up =3D get_usage_page(drvdata.hdev); u8 green, red, blue; @@ -1096,9 +1095,10 @@ static int oxp_rgb_color_set(void) u8 *data; int i; =20 - red =3D br * drvdata.led_mc->subled_info[0].intensity / max_br; - green =3D br * drvdata.led_mc->subled_info[1].intensity / max_br; - blue =3D br * drvdata.led_mc->subled_info[2].intensity / max_br; + led_mc_calc_color_components(drvdata.led_mc, br); + red =3D drvdata.led_mc->subled_info[0].brightness; + green =3D drvdata.led_mc->subled_info[1].brightness; + blue =3D drvdata.led_mc->subled_info[2].brightness; =20 switch (up) { case GEN1_USAGE_PAGE: @@ -1383,16 +1383,19 @@ static struct mc_subled oxp_rgb_subled_info[] =3D { { .color_index =3D LED_COLOR_ID_RED, .intensity =3D 0x24, + .max_intensity =3D 0xff, .channel =3D 0x1, }, { .color_index =3D LED_COLOR_ID_GREEN, .intensity =3D 0x22, + .max_intensity =3D 0xff, .channel =3D 0x2, }, { .color_index =3D LED_COLOR_ID_BLUE, .intensity =3D 0x99, + .max_intensity =3D 0xff, .channel =3D 0x3, }, }; From nobody Fri Sep 25 17:42:22 2026 Received: from mail-oo2-f42.google.com (mail-oo2-f42.google.com [74.125.231.170]) (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 A5CA539A04A for ; Thu, 10 Sep 2026 03:21:34 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789010496; cv=none; b=K3IpIxpAgzXxrXuH62ow6NB9xMM+VI3ZTHhsEUF6wzCo/SkdnD6MBYTSoOvrJWBMUP9AiZR2Avk3aG8fkj1hjVGtEjMIMqnR0MDlxx7vfaQpoBliLGosgZEL+seRJRKfQ0U5bimQl7xXH82JdbIf7L20kWqpULh8LX08A1Po5TA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789010496; c=relaxed/simple; bh=Um74UX+MRF/9P0ke3riKZo7+21YZ3eMyq0VMLteOHVg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Mbr0gB7FHatNHMwBmD5a9VwAE45mKsC4wf7JOBpWHZFk+IZ8ZT0LMSO99BW2rJoTwezRtby1+nEJet8eIE/yiIURhXMMG1Pc9ngxOJIsYTlL/O52i0LcPfwJpkcaVr9qXCl/DdQX3KO4HQvID/CIDnP9ZuA4SlBGVg27LZWn0rA= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=j6RAp1Z4; arc=none smtp.client-ip=74.125.231.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="j6RAp1Z4" Received: by mail-oo2-f42.google.com with SMTP id 46e09a7af769-7f4ea978572so268045a34.2 for ; Wed, 09 Sep 2026 20:21:34 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1789010493; x=1789615293; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=xO5C08EX71sX1hq1xlgBn85VpHzDdFalT8bt09irpJA=; b=j6RAp1Z413y0sYYZnJYJImC5XAQayzqOCxKJmSimUC43MB114oNgbG+w82A55PB5K5 GIwQ+2oyC+fWrGmVrVc7v0OcNZ1N9ZjObmwdLmdHpK6gFv1kIkl9No2O089wsA6ISert tSNOCGD0f5Ey6Z67MPY74/n9WveNoQmY6460smH8oSKBeD8JzAEsomQIGglMSjbo5Sdm KRV/k4WbVEUjz9sQjqBJuWOFpfa9SB+2xsKf8U+lxg+DEadiRdl/SL03NnbzI6C6Yw8O rOtkM7OoOfDuAbqv5wAWK34kPFUw2tIlD2WILyUyC5u9E9BwhmBhoS3cI04UmXbs8sf1 KSLg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1789010493; x=1789615293; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=xO5C08EX71sX1hq1xlgBn85VpHzDdFalT8bt09irpJA=; b=Yu88li2rykbDS1O55+vcGImvfausiM/C4SuMCGuOZBBU7Y6xUcpUECT7fikQea8Th4 C+ffKdgsrPQjjckpPZxRWZVaQ3EQr8HZCgF/gjH2JmEBfTjnG9fJDDzJISZhJ50WL5or cYsBYE9dsTGUf+EeUPlxdXcKJBvlrrjOQ+yxfVQMmHEx4MfnU3qeo9W3nlo8N8BzSTo4 i9MUODr4JL+reSNgSA2xDORWGRiljspP+8epeeB54zfIpPkIlhkAtJkaVU9OrgX/V4pH nPP2BVczvKeDHizngZ9sjHrgipMbUPl3t1IkS7o8WVzbkMcx3zNi6A57UhBZS7ttUL4V 0eWQ== X-Forwarded-Encrypted: i=1; AKwUvBzrzWzBkUy0JJwTv7VeTKjixBXRpJOmoOH2ms8Ii10V0glzHKzKIKrfvwSEb+Grpg9ZbN7WfKujIeu5EzM=@vger.kernel.org X-Gm-Message-State: AFuF++lqnUI6zKFV+RFHZMI0wA6cc9CjeKdlDWkZA3Qysm6nhTzX0Lf7 k1Gw2OqTl9hwOQYhwBPn9WwMCCLc4rzOZWXjLwYK5JJEisoD/td8IDjD X-Gm-Gg: AYBFou2d1XGFAzJwb+CYeNOXqpfxeHf1Tga6BVONTHGWqA/L8rJx7fuL/8ksz3f3LaD rAez0lEloBZNRAl3+OzVGpYMvC0qi53l9jX5iPKTEMu/6YCOZO97lJen3zWNhDUHRRQDzOUp5g/ Mx6nRzf9L0QkWL8iqOveO1Vh4Tf+21Q+7o0OVR/DiUvx7Xr3wnCBkwJNv4C8+wK8uUTicS3zT9n Vxgb5PJo+xfNy/2vfxu56TVjyn1pVd0PAqaqXA+YR+1r8VYDmkEx+HY0CxOSGf5cjgWU/hVgdUd EzWKAPpZrvlw7WHpavXCqS9fd+6KrEMK++U7oVICSh1Mwhj/NR51sVnnK0A8DRQd02ZSba7xFHE fgxs5Kjer+aYMaYWJhxG3CrFb8YatBRNVNGeeS2dxk++BLzh+31Bg93pFj+3XQfvQRvG9tHJlOh Q6axptq0OLybQEpBa1py5w1Cy3R8JhUZ1owRXwireFyCyAz1Jt4FJp7Lb86iN5WupP5ZTJrkKH2 h4oa2nUm06G57y3OEULO7GvXR2yN8i8gCZdgQy4r29Q+coWoYSCDO17aHYUzHRURw== X-Received: by 2002:a05:6830:7316:b0:7e9:bbf5:1079 with SMTP id 46e09a7af769-7fb7d276778mr17530457a34.2.1789010493406; Wed, 09 Sep 2026 20:21:33 -0700 (PDT) Received: from localhost ([76.187.192.44]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-7f9f6da9a49sm19758315a34.13.2026.09.09.20.21.32 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Wed, 09 Sep 2026 20:21:33 -0700 (PDT) From: Andrei Aldea To: Jiri Kosina , Benjamin Tissoires , "Derek J. Clark" Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Lee Jones , Pavel Machek , linux-leds@vger.kernel.org Subject: [PATCH 06/15] HID: hid-oxp: serialize complete RGB updates Date: Wed, 9 Sep 2026 22:21:06 -0500 Message-ID: <20260910032115.28669-7-andrei1998@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260910032115.28669-1-andrei1998@gmail.com> References: <20260910032115.28669-1-andrei1998@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The transport mutex protects one report at a time, but changing an RGB effect requires a status command followed by a color or effect command. Serialize whole RGB transactions so sysfs and delayed brightness work do not interleave these command sequences. Fixes: 84910c459d65 ("HID: hid-oxp: Add OneXPlayer configuration driver") Assisted-by: LLM Reviewed-by: Derek J. Clark Signed-off-by: Andrei Aldea Tested-by: Derek J. Clark --- drivers/hid/hid-oxp.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/hid/hid-oxp.c b/drivers/hid/hid-oxp.c index bee1317..8d07816 100644 --- a/drivers/hid/hid-oxp.c +++ b/drivers/hid/hid-oxp.c @@ -181,6 +181,7 @@ static struct oxp_hid_cfg { struct led_classdev_mc *led_mc; struct hid_device *hdev; struct mutex cfg_mutex; /*ensure single synchronous output report*/ + struct mutex rgb_mutex; /*serialize complete RGB transactions*/ u8 rgb_brightness; u8 gamepad_mode; u8 rumble_intensity; @@ -1074,6 +1075,8 @@ static ssize_t oxp_rgb_status_show(void) u16 up =3D get_usage_page(drvdata.hdev); u8 *data; =20 + guard(mutex)(&drvdata.rgb_mutex); + switch (up) { case GEN1_USAGE_PAGE: data =3D (u8[1]) { OXP_GET_PROPERTY }; @@ -1191,6 +1194,8 @@ static ssize_t enabled_store(struct device *dev, stru= ct device_attribute *attr, return ret; val =3D ret; =20 + guard(mutex)(&drvdata.rgb_mutex); + ret =3D oxp_rgb_status_store(val, drvdata.rgb_speed, drvdata.rgb_brightness); if (ret) @@ -1244,6 +1249,8 @@ static ssize_t effect_store(struct device *dev, struc= t device_attribute *attr, =20 val =3D ret; =20 + guard(mutex)(&drvdata.rgb_mutex); + ret =3D oxp_rgb_status_store(drvdata.rgb_en, drvdata.rgb_speed, drvdata.rgb_brightness); if (ret) @@ -1302,6 +1309,8 @@ static ssize_t speed_store(struct device *dev, struct= device_attribute *attr, if (val > 9) return -EINVAL; =20 + guard(mutex)(&drvdata.rgb_mutex); + ret =3D oxp_rgb_status_store(drvdata.rgb_en, val, drvdata.rgb_brightness); if (ret) return ret; @@ -1340,6 +1349,8 @@ static void oxp_rgb_queue_fn(struct work_struct *work) u8 val =3D 4 * brightness / max_brightness; int ret; =20 + guard(mutex)(&drvdata.rgb_mutex); + if (drvdata.rgb_brightness !=3D val) { ret =3D oxp_rgb_status_store(drvdata.rgb_en, drvdata.rgb_speed, val); if (ret) @@ -1470,6 +1481,7 @@ static int oxp_cfg_probe(struct hid_device *hdev, u16= up) =20 hid_set_drvdata(hdev, &drvdata); mutex_init(&drvdata.cfg_mutex); + mutex_init(&drvdata.rgb_mutex); drvdata.hdev =3D hdev; =20 if (up =3D=3D GEN2_USAGE_PAGE && oxp_hybrid_mcu_device()) From nobody Fri Sep 25 17:42:22 2026 Received: from mail-oo2-f42.google.com (mail-oo2-f42.google.com [74.125.231.170]) (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 D36C92931C1 for ; Thu, 10 Sep 2026 03:21:35 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789010497; cv=none; b=pNO6PK04uNoQolcOlRD+wS6Y2OAPqJk7gCbAUryCmvLrAupnX8gJuYxPBjVBCRSR21OvytWi16CQmbKxnnEn5yNdZIgDWIfwoC4OTr3vWXR0HuNknWU8l6xblEMOXPQm25VYCa4hd7x5jatoP8MBCm4l7H0bgTmCcOznQ0GLMK0= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789010497; c=relaxed/simple; bh=yo2MDFhvnhT1VSXQJAy8INcs1Je44ObhgyQM6+ip/u4=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=E4WgQ+fUDz58+g0KlMnYsKlel25N3SlFaQJtW+9/aSjf/oFbsWpQ6UwutlGTmKTKCCN8y4Ija5uiW0TpMI86XJbH2Oockc6OEcZ6IHjTifE7EQGm3qU0n7cVCUsssp8QYgQwZ6iVAoRjYBA1nr4sD1FtftVhPv8BQyF+W7yB/Lc= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=hE0GXt4B; arc=none smtp.client-ip=74.125.231.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="hE0GXt4B" Received: by mail-oo2-f42.google.com with SMTP id 46e09a7af769-7f4e6a1d3b6so301760a34.3 for ; Wed, 09 Sep 2026 20:21:35 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1789010494; x=1789615294; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=0m2EXA4RWU3Xwtz9ppvYXUdEt6FGFiWjjYdvOoJpw/g=; b=hE0GXt4BN3EG/vuf7o+8sQCrIeCL0Mv9Hk34glK9mB80bcBH3ngSbc04bfra+HHEkG ZNvOpTTxscUOQjMzKHmSPncx2hMikZ2BIzAGnHKOKYJWpj6yvdu6xSodu13Tx9vzxdYa 905q6UTHmlzFEudrsx+UBKmnUrJPdBDg4DkUcGBPcyrGdNaEuq0VoBwTh/YpYBOtr/u1 xa47DPqXo0LI5np21UIZQV9kBpoCeX3hsZJSMtRepbBTGd40iqew2+oWyv6G4xcrZ38E YhCIug2/tdJLRWn6LxWZR67+l8UuBGm1oLGj29h0bNxhDAN8IfcxTT80PpGSqmwcwj27 cdnA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1789010494; x=1789615294; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=0m2EXA4RWU3Xwtz9ppvYXUdEt6FGFiWjjYdvOoJpw/g=; b=PTWWLzvI6j+/NCVF8hoI4qaLiWsO7IM/KVp+bWGVxJ6rLh7vLy+Cqn4Hvoh0gr3Dpz QgNG+Qfp4chiuxTCNYPV8RZDyTMj/92MFmnTKZQH7T5KXCcDOowCPEub2Q/9z8zbgPY5 cT3cyBHVLEAR26cVEam39kj7QbRi3fqGxcftSKTN5VBZJfEVyXKk3LUYUlnlEJpqCTgV NBwzFko0V8vSYOC7ML6UzvciWEGS6u4ntSOYD4f/mixlHZAzJ77SWHmbE5W0eO7osttH V6ivJrrxqZhwTErrH5/S4WQEmRYd6UTJiKwLXq3ylplSOb9q7WH0GtGcW3S356DzeC4J p7rw== X-Forwarded-Encrypted: i=1; AKwUvBxHDnwEb6TpFHaUwJdUlOG9fzkl7X7mREygTtp7X9qB9cc/WE6QDuiyR+cxjEoIXQtjKHvyzeouE0y4Ur4=@vger.kernel.org X-Gm-Message-State: AFuF++mkMDds1w7DQEKEoULAv8T6YGmrbtFbt8QDO2tSHIRbPLn6s+km hHC4+nRkBmTRjLc9nM2MedWcSh/8+UxFr6nw88kXny5Am69KQFWuPv/3 X-Gm-Gg: AYBFou0PC4H20dYeWC5VAb3d4Kc493m3xJQMBHT3smuDD3mpwXrwAU8yhtYjJdVV1Ra fjz6piTrmDo5a7uvrrzrbdVOgTghVEl5qak3WXKNzvCafV/8y/YdSqHPJRHpEHrIzyJPUTIIjc6 VqLOwTXIw70J8VX/ZpYwec1nQqq21AcEsHYx9yN0TE4RU2Z/IawKMKvpFlx3t+TjrGIaQT8PJ03 kedLrzn+FXawznGXARM3MG7vaGcMBaWdFOKW73EHaT8YXP7Ftf6eGmWvL30JPiVwKVMlqtkedly /Au/HLYAyMtPbD6xHpY0dU4cgMSb4btuT0GfTJocInLiB9Q6MIBAdEItFGrNls6+N3AFfR52bSi DUJvUOaR0vX+rFPaTkbcMQ/g1J+lZxuwgH5OHo8q1FGFuAzGv5zdm6YV6cI+tn+v5fSFoSa/dv8 a3twAI6XO/OiI96GALot03M8F13GEwe06q2UHAXTHNkA8c18dEwL2SSfIUOxgPyScijuB3PApA8 aydvBGAvx71lVI1K+IY3dPF0ETA0epdIm6tY/3K8Q+494JtjfBYEdzocn4dK7zmGQ== X-Received: by 2002:a05:6830:67d2:b0:7dc:d6b1:48b7 with SMTP id 46e09a7af769-7fb7aec2172mr16246619a34.1.1789010494421; Wed, 09 Sep 2026 20:21:34 -0700 (PDT) Received: from localhost ([76.187.192.44]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-7f9f6da9a49sm19758315a34.13.2026.09.09.20.21.33 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Wed, 09 Sep 2026 20:21:34 -0700 (PDT) From: Andrei Aldea To: Jiri Kosina , Benjamin Tissoires , "Derek J. Clark" Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Lee Jones , Pavel Machek , linux-leds@vger.kernel.org Subject: [PATCH 07/15] HID: hid-oxp: select brightness policy for the new RGB effect Date: Wed, 9 Sep 2026 22:21:07 -0500 Message-ID: <20260910032115.28669-8-andrei1998@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260910032115.28669-1-andrei1998@gmail.com> References: <20260910032115.28669-1-andrei1998@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Set the requested effect before constructing its status command, since monocolor uses maximum hardware brightness and software intensity scaling. Previously entering or leaving monocolor used the old effect's brightness policy. Restore the cached effect if either output command fails. Fixes: 84910c459d65 ("HID: hid-oxp: Add OneXPlayer configuration driver") Assisted-by: LLM Reviewed-by: Derek J. Clark Signed-off-by: Andrei Aldea Tested-by: Derek J. Clark --- drivers/hid/hid-oxp.c | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/drivers/hid/hid-oxp.c b/drivers/hid/hid-oxp.c index 8d07816..9c22c8e 100644 --- a/drivers/hid/hid-oxp.c +++ b/drivers/hid/hid-oxp.c @@ -1240,6 +1240,7 @@ static DEVICE_ATTR_RO(enabled_index); static ssize_t effect_store(struct device *dev, struct device_attribute *a= ttr, const char *buf, size_t count) { + u8 old_effect; int ret; u8 val; =20 @@ -1250,15 +1251,21 @@ static ssize_t effect_store(struct device *dev, str= uct device_attribute *attr, val =3D ret; =20 guard(mutex)(&drvdata.rgb_mutex); + old_effect =3D drvdata.rgb_effect; + drvdata.rgb_effect =3D val; =20 ret =3D oxp_rgb_status_store(drvdata.rgb_en, drvdata.rgb_speed, drvdata.rgb_brightness); - if (ret) + if (ret) { + drvdata.rgb_effect =3D old_effect; return ret; + } =20 ret =3D oxp_rgb_effect_set(val); - if (ret) + if (ret) { + drvdata.rgb_effect =3D old_effect; return ret; + } =20 return count; } From nobody Fri Sep 25 17:42:22 2026 Received: from mail-oa2-f12.google.com (mail-oa2-f12.google.com [74.125.231.76]) (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 71B6339D6C9 for ; Thu, 10 Sep 2026 03:21:36 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.76 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789010498; cv=none; b=Gj7eYnVgRYucjI6bxIdroDw28IaYF9ROoM/OAXj6xk2VOBJsCOBqaAH5NUERscL7hikJCszQAVSq2vBor2zLPGjXbp6TSV3MANiLUhd2s+j1JPJfRyXBfa1mAuvKk1tIMiLT6rNNH55SB1aq0L2fbLebBzpdHM/bxUzEMUPRgV4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789010498; c=relaxed/simple; bh=aZyvmT0L698Ck/AYdKKRNtF/wvSYnHSy9X9747mK03Q=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=YjecvKAX1mxKOiwzCy4LRjCk/D4pqz/F0J7WPV5zSZIrTcCkvKtNN6tk9zXe7wbO9Lsrna17shzQIAljdwh71Nuz82IhR4umYqxa//uuaMAypHnH73aFtkLU+JF32nntyRdUkZPKONJBrlneTZnS2Tw4d7bt8VPIVZmqSu6W7yY= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=nQqyzofL; arc=none smtp.client-ip=74.125.231.76 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="nQqyzofL" Received: by mail-oa2-f12.google.com with SMTP id 586e51a60fabf-46af36be149so1557561fac.3 for ; Wed, 09 Sep 2026 20:21:36 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1789010495; x=1789615295; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=4u7cG9b1uT0ZFWN5qjbpCuu29TvPQK9V+K7Aw9iwlrA=; b=nQqyzofL/crVCt1bvgnL3Mki69FaTFxJBXcS+SljNWl8w30ogLtmA39o0BjsiIZaKB MR0wdr1aWAcKU1smYAcnVKi9Z2FpsG9Rua+jr2JD1XW6f+UxuVaCGLf2y3cTaUa90AB+ 81cSMczVWUv9ZdGjY2Uokl97C9/bSpNqHPyX5sKYPp50qbs1tgZFCaE6j2K2YTmxvwue 9A12fyniKP9oFLUrvjUJ+XbOEdi6/MOPicYJfjZz/Ai9OZAzqInRdl1jj4K5qvQaAQzj TiPBwpb0PZTwnv0k+pWxab6XRhnpi1dQIWLWijFwL+PT5GJifibLP/U8DtmweGUZsx+o dMZw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1789010495; x=1789615295; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=4u7cG9b1uT0ZFWN5qjbpCuu29TvPQK9V+K7Aw9iwlrA=; b=KWRqSqNhWr8MJG6rRPgk7Lgf1Us72+Dtp2cGRx8W9vovmPFd9SO/gZEIyAAv5Fk5i/ BKTV6oAIBKwIeA20oiwTRKi9HEliSt/l/0bxLmlgPghr+AxheP/iyaszvsHQ19STff/L m+J//07J/QBrzoehH301keyMMJ0f6KDgVLGcWtliRUqMO5Dpn7LUvuFk0cwLAOAa29vv 8WWTF+q2xYYgbG3gI9z2lh4pNkw44X2MZ4RJ2jhWNr3TcHvjZLCcTdQZVdXF2bT22Xbi umI/bhK+sDSNox0M0pkMG0s8hgh0QU/UBpfLQvta/llSVY1n/4g8VsRu/3l/uTTvoo/S 3dmg== X-Forwarded-Encrypted: i=1; AKwUvBxIP9239GX/R+tbQhO3Kk8OdJQGVaWRufmDgeZRHjdIPf47LJ+j7wYrJ+h9GBD5Rqed+GRUcKYGrbYguAo=@vger.kernel.org X-Gm-Message-State: AFuF++n7hRDvKmT2UEgeTYarDVlGONTFmI1+DuwGt6RKlKA3WNkgWgGt Uv0NWWrPPwELE+E6P9AvyG3kLWd78bcog/k3YXoS4PMvtqJFmqcAcN5x X-Gm-Gg: AYBFou3mPWgSv/cUH/VpK8eOwjUujAx9KjENjBH37VHhhriJd1VN7YVQmPmgYEi61WM Fv5WYZexKK5gjqrTUVDlyMDBtO54CE5HGYZiwLSG2WSB8iQNTgl73UlnudT/cihc/ErOfDPMzVz WI9iGbITVCgF4OZA0GagYS83hHiD4+XksJ+4IEdcYKlJwKRyU4r5/qmjB3xgF91DsXHLU8KfaDO HFVI7VGNZYg7auPnraSp22hurXTNucbPQqnbLuyqN6uOnW7h43fSsbxUrCcsUMkzIW3FiEhnv+K b7GDdDCuo7gEbb1ClgaBAghZPVOfgaIRswZLvpj2rL7/Lx3PVuUq6uG2u1slX7Vn/JmAYhns3o4 coU4S4L0wvrYYl/2pOQ6iWcBIYpvx+HDujJVKJ63lp4MmtHZZe7MvmJPEqTeyIRPzNsHGuuDSB6 1z0zB8XgOtUFHfOCrndsUDE6vGszCuPQMsY80ZwbR3e3gkbdarxIJ94ANqz1xCEHIBsYuRwGFHk JLlkE7xKgPGn5waWKMR2xRF8MvB514gXu9toh3nwJ2+IIPG34eb33I= X-Received: by 2002:a05:6830:f89:b0:7ee:363d:ce16 with SMTP id 46e09a7af769-7fb76e3cd73mr17575466a34.0.1789010495285; Wed, 09 Sep 2026 20:21:35 -0700 (PDT) Received: from localhost ([76.187.192.44]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-7f9f6da9a49sm19758315a34.13.2026.09.09.20.21.34 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Wed, 09 Sep 2026 20:21:34 -0700 (PDT) From: Andrei Aldea To: Jiri Kosina , Benjamin Tissoires , "Derek J. Clark" Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Lee Jones , Pavel Machek , linux-leds@vger.kernel.org Subject: [PATCH 08/15] HID: hid-oxp: stop configuration work during teardown Date: Wed, 9 Sep 2026 22:21:08 -0500 Message-ID: <20260910032115.28669-9-andrei1998@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260910032115.28669-1-andrei1998@gmail.com> References: <20260910032115.28669-1-andrei1998@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Configuration callbacks can queue delayed RGB, button-mapping, and MCU initialization work. Normal removal previously closed the HID transport without synchronously stopping that work, while a later configuration probe failure could leave work queued as devres released its objects. Track which work items have been initialized, reject new output once teardown begins, disable initialized work synchronously, and drain an in-flight transport write before closing the device. Use the same shutdown path for normal removal and every probe-error unwind after work can have been exposed. This also avoids operating on uninitialized work for HID interfaces that do not own configuration state. Fixes: 84910c459d65 ("HID: hid-oxp: Add OneXPlayer configuration driver") Assisted-by: LLM Reviewed-by: Derek J. Clark Signed-off-by: Andrei Aldea Tested-by: Derek J. Clark --- drivers/hid/hid-oxp.c | 94 +++++++++++++++++++++++++++++++++++-------- 1 file changed, 77 insertions(+), 17 deletions(-) diff --git a/drivers/hid/hid-oxp.c b/drivers/hid/hid-oxp.c index 9c22c8e..eef57c3 100644 --- a/drivers/hid/hid-oxp.c +++ b/drivers/hid/hid-oxp.c @@ -188,6 +188,9 @@ static struct oxp_hid_cfg { u8 rgb_effect; u8 rgb_speed; u8 rgb_en; + bool rgb_work_initialized; + bool gen2_work_initialized; + bool removing; } drvdata; =20 #define OXP_FILL_PAGE_SLOT(page, btn) \ @@ -320,7 +323,7 @@ static int oxp_hid_raw_event_gen_1(struct hid_device *h= dev, struct led_classdev_mc *led_mc =3D drvdata.led_mc; struct oxp_gen_1_rgb_report *rgb_rep; =20 - if (size < sizeof(*rgb_rep)) + if (size < sizeof(*rgb_rep) || !led_mc) return 0; =20 if (data[1] !=3D OXP_FID_GEN1_RGB_REPLY) @@ -360,6 +363,9 @@ static void oxp_mcu_init_fn(struct work_struct *work) u8 gp_mode_data[3] =3D { OXP_GP_MODE_DEBUG, 0x01, 0x02 }; int ret; =20 + if (READ_ONCE(drvdata.removing)) + return; + /* Re-apply the button mapping */ ret =3D oxp_set_buttons(); if (ret) @@ -406,13 +412,16 @@ static int oxp_hid_raw_event_gen_2(struct hid_device = *hdev, * Re-apply our settings after this has been received. */ if (data[3] =3D=3D OXP_EFFECT_MONO_TRUE) { - mod_delayed_work(system_dfl_wq, &drvdata.oxp_mcu_init, msecs_to_jiffies(= 50)); + if (READ_ONCE(drvdata.gen2_work_initialized) && + !READ_ONCE(drvdata.removing)) + mod_delayed_work(system_dfl_wq, &drvdata.oxp_mcu_init, + msecs_to_jiffies(50)); return 0; } =20 if (data[3] !=3D OXP_GET_PROPERTY) return 0; - if (size < sizeof(*rgb_rep)) + if (size < sizeof(*rgb_rep) || !led_mc) return 0; =20 rgb_rep =3D (struct oxp_gen_2_rgb_report *)data; @@ -449,6 +458,9 @@ static int oxp_hid_raw_event(struct hid_device *hdev, s= truct hid_report *report, { u16 up =3D get_usage_page(hdev); =20 + if (!hid_get_drvdata(hdev) || READ_ONCE(drvdata.removing)) + return 0; + dev_dbg(&hdev->dev, "raw event data: [%*ph]\n", size, data); =20 switch (up) { @@ -476,6 +488,9 @@ static int mcu_property_out(u8 *header, size_t header_s= ize, u8 *data, return -EINVAL; =20 guard(mutex)(&drvdata.cfg_mutex); + if (READ_ONCE(drvdata.removing)) + return -ENODEV; + memcpy(dmabuf, header, header_size); memcpy(dmabuf + header_size, data, data_size); if (footer_size) @@ -717,6 +732,9 @@ static void oxp_btn_queue_fn(struct work_struct *work) { int ret; =20 + if (READ_ONCE(drvdata.removing)) + return; + ret =3D oxp_set_buttons(); if (ret) dev_err(&drvdata.hdev->dev, @@ -802,7 +820,9 @@ static ssize_t map_button_store(struct device *dev, default: return -EINVAL; } - mod_delayed_work(system_dfl_wq, &drvdata.oxp_btn_queue, msecs_to_jiffies(= 50)); + if (!READ_ONCE(drvdata.removing)) + mod_delayed_work(system_dfl_wq, &drvdata.oxp_btn_queue, + msecs_to_jiffies(50)); return count; } =20 @@ -1356,6 +1376,9 @@ static void oxp_rgb_queue_fn(struct work_struct *work) u8 val =3D 4 * brightness / max_brightness; int ret; =20 + if (READ_ONCE(drvdata.removing)) + return; + guard(mutex)(&drvdata.rgb_mutex); =20 if (drvdata.rgb_brightness !=3D val) { @@ -1379,6 +1402,9 @@ static void oxp_rgb_queue_fn(struct work_struct *work) static void oxp_rgb_brightness_set(struct led_classdev *led_cdev, enum led_brightness brightness) { + if (READ_ONCE(drvdata.removing)) + return; + led_cdev->brightness =3D brightness; mod_delayed_work(system_dfl_wq, &drvdata.oxp_rgb_queue, msecs_to_jiffies(= 50)); } @@ -1480,6 +1506,24 @@ static bool oxp_hybrid_mcu_device(void) return quirks->hybrid_mcu; } =20 +static void oxp_drain_output(void) +{ + /* Wait for any in-flight sysfs output before closing the transport. */ + guard(mutex)(&drvdata.cfg_mutex); +} + +static void oxp_quiesce_work(void) +{ + WRITE_ONCE(drvdata.removing, true); + if (drvdata.rgb_work_initialized) + disable_delayed_work_sync(&drvdata.oxp_rgb_queue); + if (drvdata.gen2_work_initialized) { + disable_delayed_work_sync(&drvdata.oxp_btn_queue); + disable_delayed_work_sync(&drvdata.oxp_mcu_init); + } + oxp_drain_output(); +} + static int oxp_cfg_probe(struct hid_device *hdev, u16 up) { struct oxp_bmap_page_1 *bmap_1; @@ -1490,6 +1534,7 @@ static int oxp_cfg_probe(struct hid_device *hdev, u16= up) mutex_init(&drvdata.cfg_mutex); mutex_init(&drvdata.rgb_mutex); drvdata.hdev =3D hdev; + drvdata.removing =3D false; =20 if (up =3D=3D GEN2_USAGE_PAGE && oxp_hybrid_mcu_device()) goto skip_rgb; @@ -1497,16 +1542,21 @@ static int oxp_cfg_probe(struct hid_device *hdev, u= 16 up) drvdata.led_mc =3D &oxp_cdev_rgb; =20 INIT_DELAYED_WORK(&drvdata.oxp_rgb_queue, oxp_rgb_queue_fn); + drvdata.rgb_work_initialized =3D true; ret =3D devm_led_classdev_multicolor_register(&hdev->dev, &oxp_cdev_rgb); - if (ret) - return dev_err_probe(&hdev->dev, ret, + if (ret) { + dev_err_probe(&hdev->dev, ret, "Failed to create RGB device\n"); + goto err_quiesce; + } =20 ret =3D devm_device_add_group(drvdata.led_mc->led_cdev.dev, &oxp_rgb_attr_group); - if (ret) - return dev_err_probe(drvdata.led_mc->led_cdev.dev, ret, + if (ret) { + dev_err_probe(drvdata.led_mc->led_cdev.dev, ret, "Failed to create RGB configuration attributes\n"); + goto err_quiesce; + } =20 ret =3D oxp_rgb_status_show(); if (ret) @@ -1519,14 +1569,18 @@ static int oxp_cfg_probe(struct hid_device *hdev, u= 16 up) =20 skip_rgb: bmap_1 =3D devm_kzalloc(&hdev->dev, sizeof(struct oxp_bmap_page_1), GFP_K= ERNEL); - if (!bmap_1) - return dev_err_probe(&hdev->dev, -ENOMEM, + if (!bmap_1) { + ret =3D dev_err_probe(&hdev->dev, -ENOMEM, "Unable to allocate button map page 1\n"); + goto err_quiesce; + } =20 bmap_2 =3D devm_kzalloc(&hdev->dev, sizeof(struct oxp_bmap_page_2), GFP_K= ERNEL); - if (!bmap_2) - return dev_err_probe(&hdev->dev, -ENOMEM, + if (!bmap_2) { + ret =3D dev_err_probe(&hdev->dev, -ENOMEM, "Unable to allocate button map page 2\n"); + goto err_quiesce; + } =20 drvdata.bmap_1 =3D bmap_1; drvdata.bmap_2 =3D bmap_2; @@ -1537,14 +1591,21 @@ skip_rgb: drvdata.rumble_intensity =3D 5; =20 INIT_DELAYED_WORK(&drvdata.oxp_mcu_init, oxp_mcu_init_fn); + WRITE_ONCE(drvdata.gen2_work_initialized, true); mod_delayed_work(system_dfl_wq, &drvdata.oxp_mcu_init, msecs_to_jiffies(5= 0)); =20 ret =3D devm_device_add_group(&hdev->dev, &oxp_cfg_attrs_group); - if (ret) - return dev_err_probe(&hdev->dev, ret, + if (ret) { + dev_err_probe(&hdev->dev, ret, "Failed to attach configuration attributes\n"); + goto err_quiesce; + } =20 return 0; + +err_quiesce: + oxp_quiesce_work(); + return ret; } =20 static int oxp_hid_probe(struct hid_device *hdev, @@ -1587,9 +1648,8 @@ static int oxp_hid_probe(struct hid_device *hdev, =20 static void oxp_hid_remove(struct hid_device *hdev) { - cancel_delayed_work(&drvdata.oxp_rgb_queue); - cancel_delayed_work(&drvdata.oxp_btn_queue); - cancel_delayed_work(&drvdata.oxp_mcu_init); + if (hid_get_drvdata(hdev)) + oxp_quiesce_work(); hid_hw_close(hdev); hid_hw_stop(hdev); } From nobody Fri Sep 25 17:42:22 2026 Received: from mail-oi2-f12.google.com (mail-oi2-f12.google.com [74.125.231.204]) (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 A33163A1696 for ; Thu, 10 Sep 2026 03:21:37 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.204 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789010502; cv=none; b=XEnjVFZQSEtB2m6YNzpb5ja0TWrB+Ul5AhHbb5H9Kom1o3B5i2neBYrdOLEkyTMjTrrLFDllL5w/76DA3Lq5ajDNcXzrd7b3BV6oMUHuxHX9Q4RxYTAKwZnozfAzYz7SDEYSH0fnKIGqHmiN+GjNSgH7OlmxtXisxuuRUbqqIaU= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789010502; c=relaxed/simple; bh=XmFFMhTjTh5nV2Y901ZgnaijIIASE6EGhT1b42GCpfM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=CAtEq52wcYtr9u8c4zwJypzXKLacVcPlt2ibaaVe/5w5JDrVJMQvcs6wZgVAAURKezaCWpf4rP7/VYaZn9zSMhsxaaCthJ32i48hgSfGV6ODmkvjac7yO4KHumMWV08FNZ1qC/heRzX44QklSbg4snRnZG7eBMCZ6/RzcfP+9is= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=g2qmiEWG; arc=none smtp.client-ip=74.125.231.204 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="g2qmiEWG" Received: by mail-oi2-f12.google.com with SMTP id 5614622812f47-4b375b957e0so357066b6e.2 for ; Wed, 09 Sep 2026 20:21:37 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1789010496; x=1789615296; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=zkGhhTPGs/rCuO1ZbrPY1q+0VNv+WQ1he9wySO192h8=; b=g2qmiEWGnr9mV5+xjgnODGUQAgNcbkxhHaz8ogb84xZia2nSp25Nt/6avw3mg6MEFB /CvelcWeQIFjNWmUwPFn+OoQUkOM32rXuxctsQf16y8rpGMi2tn95UKR7hZdNQ/Hsprc 8tNXUURCn0huyTH/6bEVyubVmebupJE8xRtKEI8S/2khKxMKgc9LHU8yM6KZlmTHTBpJ UYNM+kI2WpoRPsYiJeoXcsq3UYloCn1BvulnWu/MnQUlggw50OnDbr9flS8FvKbD2PIT aYZalQCnOuGlddm0iozc1plnuaivjWSN6Ci1TWjuYXSga/u2eeUjpkkBQr5iB4OKXZWh pfqA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1789010496; x=1789615296; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=zkGhhTPGs/rCuO1ZbrPY1q+0VNv+WQ1he9wySO192h8=; b=rACytSrvpS7nlzO3P4pYbxo1QU7WnXA3hSvLF5NsKnz4dbwkKT0X+ZmWpn4XlIkhZj ZsyZQwXBd1NGVzS9OLlfTnXgvVD9CjlaMJGKWzy5igosyzl5e6EvBXjqKHK+vGUaZykf 2tVT4nI5nelmFR2iUm+0DYkcT3Gp+aGzLqGsDdOIwLu61wCdjfndFQgTe5VVJutVZIVh O7XJ2sctoBylC1Bhzj/ZI3hD4mxooyLGVACblRNUghGbJz1VYKSfUi2W095mFEXin5+/ G6Z1VVO9+i7YIshFWtkxkQnTRpBdg1J2ADUra1VE92X/pmTpuJFzNfUMlaq/qEilvoDI CSYg== X-Forwarded-Encrypted: i=1; AKwUvBwDdyhopybgpEVt/WC88ntEAFkp8j3ymiWQspwdKUUjjfskuMNy9bmtBnIjpnGKKzG4jLy4RQv84BUakJQ=@vger.kernel.org X-Gm-Message-State: AFuF++ntEiwIVQ79o+a5i1sPugAT6Eh6aTuxK93Kz0WzBO4m9fJD+jaJ p9MyN28aye6E24zU8p/hwfAn1MP0r5JNNVS8cnNVxwe+5FGnsxNaJquP X-Gm-Gg: AYBFou2Ho+ytxuIDHwh2/8RR85uxOS0LRVpo50xQ6IcpwiJs01EwKL95gDAWNUpEo2o OIOlOcVwxSwcXhVEUVEmqd9RJsOixIjQMjeEhfVKyIt5kYv/CO9BwWrxvB2PIH0gLW+HNQPPxwJ gxuX5MYLy/TpHKtCtDKrBiVkv+MQG3MlRBJXNvrons7DV7ah5f50cuXz5pJjg/gOu3cDBxHh4uZ ldGN5P2HO6uN+NdU1DWKmDA+NaPP2ijl4sLE0hVmPokVOxrhi9mEYCvKPs1IIM+mgvGD7t8lx6N 6dk+QY7eYVuVgBqUczyNKPOXIpVi/VaQpsnZ8tBKGKZ9qSpjaK77XyzQPaV6n7NTkuauf3pVKvo ZudSrVn4E7Ya74EO+uoDBVk6ro5Ef5PB7ZbuNAis6DrK3ITD5KmslpIm0Yk+MWnTRj+fR5S5PpT 0MbLTpP+tXJcYBDK2/akP82OOuoBLFlBHWXFKZNnn/btmAWu+JF+2bzaERYcly5MSxtX9gpQD6B D84ZN1iaMCNsKw+3USSyZDawfOSI5FFxy0d+mjS7klR7faeoIVnu+U= X-Received: by 2002:a05:6830:368a:b0:7f4:ede0:e3e5 with SMTP id 46e09a7af769-80272d057cdmr2141592a34.2.1789010496240; Wed, 09 Sep 2026 20:21:36 -0700 (PDT) Received: from localhost ([76.187.192.44]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-7f9f6da9a49sm19758315a34.13.2026.09.09.20.21.35 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Wed, 09 Sep 2026 20:21:35 -0700 (PDT) From: Andrei Aldea To: Jiri Kosina , Benjamin Tissoires , "Derek J. Clark" Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Lee Jones , Pavel Machek , linux-leds@vger.kernel.org Subject: [PATCH 09/15] HID: hid-oxp: keep configuration state per HID interface Date: Wed, 9 Sep 2026 22:21:09 -0500 Message-ID: <20260910032115.28669-10-andrei1998@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260910032115.28669-1-andrei1998@gmail.com> References: <20260910032115.28669-1-andrei1998@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Replace the shared driver state and mutable LED object with devm-managed state owned by each configuration HID. Resolve callbacks through their HID, LED or embedded work object instead of the last interface probed. Hybrid devices have distinct Gen1 RGB and Gen2 controller interfaces. Sharing the transport pointer and work state lets one overwrite the other. Keep the HID drvdata pointer valid until LED and sysfs objects have been released, then clear it before freeing the configuration allocation. Fixes: 252c4bf1d931 ("HID: hid-oxp: Add Second Generation RGB Control") Assisted-by: LLM Reviewed-by: Derek J. Clark Signed-off-by: Andrei Aldea Tested-by: Derek J. Clark --- drivers/hid/hid-oxp.c | 498 +++++++++++++++++++++++------------------- 1 file changed, 279 insertions(+), 219 deletions(-) diff --git a/drivers/hid/hid-oxp.c b/drivers/hid/hid-oxp.c index eef57c3..7b36687 100644 --- a/drivers/hid/hid-oxp.c +++ b/drivers/hid/hid-oxp.c @@ -172,7 +172,10 @@ struct oxp_bmap_page_2 { struct oxp_button_idx btn_m2; } __packed; =20 -static struct oxp_hid_cfg { +/* Hybrid devices expose RGB and controller configuration on separate HIDs= . */ +struct oxp_hid_cfg { + struct led_classdev_mc cdev; + struct mc_subled subled_info[3]; struct delayed_work oxp_rgb_queue; struct delayed_work oxp_btn_queue; struct oxp_bmap_page_1 *bmap_1; @@ -191,7 +194,7 @@ static struct oxp_hid_cfg { bool rgb_work_initialized; bool gen2_work_initialized; bool removing; -} drvdata; +}; =20 #define OXP_FILL_PAGE_SLOT(page, btn) \ { .button_idx =3D (page)->btn.button_idx, \ @@ -320,7 +323,8 @@ static int oxp_hid_raw_event_gen_1(struct hid_device *h= dev, struct hid_report *report, u8 *data, int size) { - struct led_classdev_mc *led_mc =3D drvdata.led_mc; + struct oxp_hid_cfg *cfg =3D hid_get_drvdata(hdev); + struct led_classdev_mc *led_mc =3D cfg->led_mc; struct oxp_gen_1_rgb_report *rgb_rep; =20 if (size < sizeof(*rgb_rep) || !led_mc) @@ -331,13 +335,13 @@ static int oxp_hid_raw_event_gen_1(struct hid_device = *hdev, =20 rgb_rep =3D (struct oxp_gen_1_rgb_report *)data; /* Ensure we save monocolor as the list value */ - drvdata.rgb_effect =3D rgb_rep->effect =3D=3D OXP_EFFECT_MONO_TRUE ? + cfg->rgb_effect =3D rgb_rep->effect =3D=3D OXP_EFFECT_MONO_TRUE ? OXP_EFFECT_MONO_LIST : rgb_rep->effect; - drvdata.rgb_speed =3D rgb_rep->speed; - drvdata.rgb_en =3D rgb_rep->enabled =3D=3D 0 ? OXP_FEAT_DISABLED : + cfg->rgb_speed =3D rgb_rep->speed; + cfg->rgb_en =3D rgb_rep->enabled =3D=3D 0 ? OXP_FEAT_DISABLED : OXP_FEAT_ENABLED; - drvdata.rgb_brightness =3D rgb_rep->brightness; + cfg->rgb_brightness =3D rgb_rep->brightness; led_mc->led_cdev.brightness =3D rgb_rep->brightness * led_mc->led_cdev.max_brightness / 4; /* If monocolor had less than 100% brightness on the previous boot, @@ -354,44 +358,48 @@ static int oxp_hid_raw_event_gen_1(struct hid_device = *hdev, return 0; } =20 -static int oxp_gen_2_property_out(enum oxp_function_index fid, u8 *data, u= 8 data_size); -static int oxp_set_buttons(void); -static int oxp_rumble_intensity_set(u8 intensity); +static int oxp_gen_2_property_out(struct oxp_hid_cfg *cfg, + enum oxp_function_index fid, u8 *data, + u8 data_size); +static int oxp_set_buttons(struct oxp_hid_cfg *cfg); +static int oxp_rumble_intensity_set(struct oxp_hid_cfg *cfg, u8 intensity); =20 static void oxp_mcu_init_fn(struct work_struct *work) { + struct oxp_hid_cfg *cfg =3D container_of(to_delayed_work(work), + struct oxp_hid_cfg, oxp_mcu_init); u8 gp_mode_data[3] =3D { OXP_GP_MODE_DEBUG, 0x01, 0x02 }; int ret; =20 - if (READ_ONCE(drvdata.removing)) + if (READ_ONCE(cfg->removing)) return; =20 /* Re-apply the button mapping */ - ret =3D oxp_set_buttons(); + ret =3D oxp_set_buttons(cfg); if (ret) - dev_err(&drvdata.hdev->dev, + dev_err(&cfg->hdev->dev, "Error: Failed to set button mapping: %i\n", ret); =20 /* Cycle the gamepad mode */ - ret =3D oxp_gen_2_property_out(OXP_FID_GEN2_TOGGLE_MODE, gp_mode_data, 3); + ret =3D oxp_gen_2_property_out(cfg, OXP_FID_GEN2_TOGGLE_MODE, gp_mode_dat= a, 3); if (ret) - dev_err(&drvdata.hdev->dev, + dev_err(&cfg->hdev->dev, "Error: Failed to set gamepad mode: %i\n", ret); =20 /* Remainder only applies for xinput mode */ - if (drvdata.gamepad_mode =3D=3D OXP_GP_MODE_DEBUG) + if (cfg->gamepad_mode =3D=3D OXP_GP_MODE_DEBUG) return; =20 gp_mode_data[0] =3D OXP_GP_MODE_XINPUT; - ret =3D oxp_gen_2_property_out(OXP_FID_GEN2_TOGGLE_MODE, gp_mode_data, 3); + ret =3D oxp_gen_2_property_out(cfg, OXP_FID_GEN2_TOGGLE_MODE, gp_mode_dat= a, 3); if (ret) - dev_err(&drvdata.hdev->dev, + dev_err(&cfg->hdev->dev, "Error: Failed to set gamepad mode: %i\n", ret); =20 /* Set vibration level */ - ret =3D oxp_rumble_intensity_set(drvdata.rumble_intensity); + ret =3D oxp_rumble_intensity_set(cfg, cfg->rumble_intensity); if (ret) - dev_err(&drvdata.hdev->dev, + dev_err(&cfg->hdev->dev, "Error: Failed to set rumble intensity: %i\n", ret); } =20 @@ -399,7 +407,8 @@ static int oxp_hid_raw_event_gen_2(struct hid_device *h= dev, struct hid_report *report, u8 *data, int size) { - struct led_classdev_mc *led_mc =3D drvdata.led_mc; + struct oxp_hid_cfg *cfg =3D hid_get_drvdata(hdev); + struct led_classdev_mc *led_mc =3D cfg->led_mc; struct oxp_gen_2_rgb_report *rgb_rep; =20 if (size < OXP_STATUS_HEADER_SIZE) @@ -412,9 +421,9 @@ static int oxp_hid_raw_event_gen_2(struct hid_device *h= dev, * Re-apply our settings after this has been received. */ if (data[3] =3D=3D OXP_EFFECT_MONO_TRUE) { - if (READ_ONCE(drvdata.gen2_work_initialized) && - !READ_ONCE(drvdata.removing)) - mod_delayed_work(system_dfl_wq, &drvdata.oxp_mcu_init, + if (READ_ONCE(cfg->gen2_work_initialized) && + !READ_ONCE(cfg->removing)) + mod_delayed_work(system_dfl_wq, &cfg->oxp_mcu_init, msecs_to_jiffies(50)); return 0; } @@ -430,13 +439,13 @@ static int oxp_hid_raw_event_gen_2(struct hid_device = *hdev, return 0; =20 /* Ensure we save monocolor as the list value */ - drvdata.rgb_effect =3D rgb_rep->effect =3D=3D OXP_EFFECT_MONO_TRUE ? + cfg->rgb_effect =3D rgb_rep->effect =3D=3D OXP_EFFECT_MONO_TRUE ? OXP_EFFECT_MONO_LIST : rgb_rep->effect; - drvdata.rgb_speed =3D rgb_rep->speed; - drvdata.rgb_en =3D rgb_rep->enabled =3D=3D 0 ? OXP_FEAT_DISABLED : + cfg->rgb_speed =3D rgb_rep->speed; + cfg->rgb_en =3D rgb_rep->enabled =3D=3D 0 ? OXP_FEAT_DISABLED : OXP_FEAT_ENABLED; - drvdata.rgb_brightness =3D rgb_rep->brightness; + cfg->rgb_brightness =3D rgb_rep->brightness; led_mc->led_cdev.brightness =3D rgb_rep->brightness * led_mc->led_cdev.max_brightness / 4; /* If monocolor had less than 100% brightness on the previous boot, @@ -456,9 +465,10 @@ static int oxp_hid_raw_event_gen_2(struct hid_device *= hdev, static int oxp_hid_raw_event(struct hid_device *hdev, struct hid_report *r= eport, u8 *data, int size) { + struct oxp_hid_cfg *cfg =3D hid_get_drvdata(hdev); u16 up =3D get_usage_page(hdev); =20 - if (!hid_get_drvdata(hdev) || READ_ONCE(drvdata.removing)) + if (!cfg || READ_ONCE(cfg->removing)) return 0; =20 dev_dbg(&hdev->dev, "raw event data: [%*ph]\n", size, data); @@ -475,7 +485,7 @@ static int oxp_hid_raw_event(struct hid_device *hdev, s= truct hid_report *report, return 0; } =20 -static int mcu_property_out(u8 *header, size_t header_size, u8 *data, +static int mcu_property_out(struct oxp_hid_cfg *cfg, u8 *header, size_t he= ader_size, u8 *data, size_t data_size, u8 *footer, size_t footer_size) { unsigned char *dmabuf __free(kfree) =3D kzalloc(OXP_PACKET_SIZE, GFP_KERN= EL); @@ -487,8 +497,8 @@ static int mcu_property_out(u8 *header, size_t header_s= ize, u8 *data, if (header_size + data_size + footer_size > OXP_PACKET_SIZE) return -EINVAL; =20 - guard(mutex)(&drvdata.cfg_mutex); - if (READ_ONCE(drvdata.removing)) + guard(mutex)(&cfg->cfg_mutex); + if (READ_ONCE(cfg->removing)) return -ENODEV; =20 memcpy(dmabuf, header, header_size); @@ -496,9 +506,9 @@ static int mcu_property_out(u8 *header, size_t header_s= ize, u8 *data, if (footer_size) memcpy(dmabuf + OXP_PACKET_SIZE - footer_size, footer, footer_size); =20 - dev_dbg(&drvdata.hdev->dev, "raw data: [%*ph]\n", OXP_PACKET_SIZE, dmabuf= ); + dev_dbg(&cfg->hdev->dev, "raw data: [%*ph]\n", OXP_PACKET_SIZE, dmabuf); =20 - ret =3D hid_hw_output_report(drvdata.hdev, dmabuf, OXP_PACKET_SIZE); + ret =3D hid_hw_output_report(cfg->hdev, dmabuf, OXP_PACKET_SIZE); if (ret < 0) return ret; =20 @@ -507,16 +517,16 @@ static int mcu_property_out(u8 *header, size_t header= _size, u8 *data, return ret =3D=3D OXP_PACKET_SIZE ? 0 : -EIO; } =20 -static int oxp_gen_1_property_out(enum oxp_function_index fid, u8 *data, +static int oxp_gen_1_property_out(struct oxp_hid_cfg *cfg, enum oxp_functi= on_index fid, u8 *data, u8 data_size) { u8 header[] =3D { fid, GEN1_MESSAGE_ID }; size_t header_size =3D ARRAY_SIZE(header); =20 - return mcu_property_out(header, header_size, data, data_size, NULL, 0); + return mcu_property_out(cfg, header, header_size, data, data_size, NULL, = 0); } =20 -static int oxp_gen_2_property_out(enum oxp_function_index fid, u8 *data, +static int oxp_gen_2_property_out(struct oxp_hid_cfg *cfg, enum oxp_functi= on_index fid, u8 *data, u8 data_size) { u8 header[] =3D { fid, GEN2_MESSAGE_ID, 0x01 }; @@ -524,7 +534,7 @@ static int oxp_gen_2_property_out(enum oxp_function_ind= ex fid, u8 *data, size_t header_size =3D ARRAY_SIZE(header); size_t footer_size =3D ARRAY_SIZE(footer); =20 - return mcu_property_out(header, header_size, data, data_size, footer, + return mcu_property_out(cfg, header, header_size, data, data_size, footer, footer_size); } =20 @@ -532,7 +542,8 @@ static ssize_t gamepad_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { - u16 up =3D get_usage_page(drvdata.hdev); + struct oxp_hid_cfg *cfg =3D dev_get_drvdata(dev); + u16 up =3D get_usage_page(cfg->hdev); u8 data[3] =3D { 0x00, 0x01, 0x02 }; int ret =3D -EINVAL; int i; @@ -551,17 +562,17 @@ static ssize_t gamepad_mode_store(struct device *dev, =20 data[0] =3D ret; =20 - ret =3D oxp_gen_2_property_out(OXP_FID_GEN2_TOGGLE_MODE, data, 3); + ret =3D oxp_gen_2_property_out(cfg, OXP_FID_GEN2_TOGGLE_MODE, data, 3); if (ret) return ret; =20 - drvdata.gamepad_mode =3D data[0]; + cfg->gamepad_mode =3D data[0]; =20 - if (drvdata.gamepad_mode =3D=3D OXP_GP_MODE_DEBUG) + if (cfg->gamepad_mode =3D=3D OXP_GP_MODE_DEBUG) return count; =20 /* Re-apply rumble settings as switching gamepad mode will override */ - ret =3D oxp_rumble_intensity_set(drvdata.rumble_intensity); + ret =3D oxp_rumble_intensity_set(cfg, cfg->rumble_intensity); if (ret) return ret; =20 @@ -571,7 +582,9 @@ static ssize_t gamepad_mode_store(struct device *dev, static ssize_t gamepad_mode_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sysfs_emit(buf, "%s\n", oxp_gamepad_mode_text[drvdata.gamepad_mode= ]); + struct oxp_hid_cfg *cfg =3D dev_get_drvdata(dev); + + return sysfs_emit(buf, "%s\n", oxp_gamepad_mode_text[cfg->gamepad_mode]); } static DEVICE_ATTR_RW(gamepad_mode); =20 @@ -656,60 +669,61 @@ static void oxp_page_fill_data(char *buf, const struc= t oxp_button_idx *buttons, } } =20 -static int oxp_set_buttons(void) +static int oxp_set_buttons(struct oxp_hid_cfg *cfg) { u8 page_1[59] =3D { 0x02, 0x38, 0x20, 0x01, 0x01 }; u8 page_2[59] =3D { 0x02, 0x38, 0x20, 0x02, 0x01 }; - u16 up =3D get_usage_page(drvdata.hdev); + u16 up =3D get_usage_page(cfg->hdev); int ret; =20 if (up !=3D GEN2_USAGE_PAGE) return -EINVAL; =20 const struct oxp_button_idx p1[] =3D { - OXP_FILL_PAGE_SLOT(drvdata.bmap_1, btn_a), - OXP_FILL_PAGE_SLOT(drvdata.bmap_1, btn_b), - OXP_FILL_PAGE_SLOT(drvdata.bmap_1, btn_x), - OXP_FILL_PAGE_SLOT(drvdata.bmap_1, btn_y), - OXP_FILL_PAGE_SLOT(drvdata.bmap_1, btn_lb), - OXP_FILL_PAGE_SLOT(drvdata.bmap_1, btn_rb), - OXP_FILL_PAGE_SLOT(drvdata.bmap_1, btn_lt), - OXP_FILL_PAGE_SLOT(drvdata.bmap_1, btn_rt), - OXP_FILL_PAGE_SLOT(drvdata.bmap_1, btn_start), + OXP_FILL_PAGE_SLOT(cfg->bmap_1, btn_a), + OXP_FILL_PAGE_SLOT(cfg->bmap_1, btn_b), + OXP_FILL_PAGE_SLOT(cfg->bmap_1, btn_x), + OXP_FILL_PAGE_SLOT(cfg->bmap_1, btn_y), + OXP_FILL_PAGE_SLOT(cfg->bmap_1, btn_lb), + OXP_FILL_PAGE_SLOT(cfg->bmap_1, btn_rb), + OXP_FILL_PAGE_SLOT(cfg->bmap_1, btn_lt), + OXP_FILL_PAGE_SLOT(cfg->bmap_1, btn_rt), + OXP_FILL_PAGE_SLOT(cfg->bmap_1, btn_start), }; =20 const struct oxp_button_idx p2[] =3D { - OXP_FILL_PAGE_SLOT(drvdata.bmap_2, btn_select), - OXP_FILL_PAGE_SLOT(drvdata.bmap_2, btn_l3), - OXP_FILL_PAGE_SLOT(drvdata.bmap_2, btn_r3), - OXP_FILL_PAGE_SLOT(drvdata.bmap_2, btn_dup), - OXP_FILL_PAGE_SLOT(drvdata.bmap_2, btn_ddown), - OXP_FILL_PAGE_SLOT(drvdata.bmap_2, btn_dleft), - OXP_FILL_PAGE_SLOT(drvdata.bmap_2, btn_dright), - OXP_FILL_PAGE_SLOT(drvdata.bmap_2, btn_m1), - OXP_FILL_PAGE_SLOT(drvdata.bmap_2, btn_m2), + OXP_FILL_PAGE_SLOT(cfg->bmap_2, btn_select), + OXP_FILL_PAGE_SLOT(cfg->bmap_2, btn_l3), + OXP_FILL_PAGE_SLOT(cfg->bmap_2, btn_r3), + OXP_FILL_PAGE_SLOT(cfg->bmap_2, btn_dup), + OXP_FILL_PAGE_SLOT(cfg->bmap_2, btn_ddown), + OXP_FILL_PAGE_SLOT(cfg->bmap_2, btn_dleft), + OXP_FILL_PAGE_SLOT(cfg->bmap_2, btn_dright), + OXP_FILL_PAGE_SLOT(cfg->bmap_2, btn_m1), + OXP_FILL_PAGE_SLOT(cfg->bmap_2, btn_m2), }; =20 oxp_page_fill_data(page_1, p1, ARRAY_SIZE(p1)); oxp_page_fill_data(page_2, p2, ARRAY_SIZE(p2)); =20 - ret =3D oxp_gen_2_property_out(OXP_FID_GEN2_KEY_STATE, page_1, ARRAY_SIZE= (page_1)); + ret =3D oxp_gen_2_property_out(cfg, OXP_FID_GEN2_KEY_STATE, page_1, ARRAY= _SIZE(page_1)); if (ret) return ret; =20 - return oxp_gen_2_property_out(OXP_FID_GEN2_KEY_STATE, page_2, ARRAY_SIZE(= page_2)); + return oxp_gen_2_property_out(cfg, OXP_FID_GEN2_KEY_STATE, page_2, ARRAY_= SIZE(page_2)); } =20 -static void oxp_reset_buttons(void) +static void oxp_reset_buttons(struct oxp_hid_cfg *cfg) { - oxp_set_defaults_bmap_1(drvdata.bmap_1); - oxp_set_defaults_bmap_2(drvdata.bmap_2); + oxp_set_defaults_bmap_1(cfg->bmap_1); + oxp_set_defaults_bmap_2(cfg->bmap_2); } =20 static ssize_t reset_buttons_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { + struct oxp_hid_cfg *cfg =3D dev_get_drvdata(dev); int val, ret; =20 ret =3D kstrtoint(buf, 10, &val); @@ -719,8 +733,8 @@ static ssize_t reset_buttons_store(struct device *dev, if (val !=3D 1) return -EINVAL; =20 - oxp_reset_buttons(); - ret =3D oxp_set_buttons(); + oxp_reset_buttons(cfg); + ret =3D oxp_set_buttons(cfg); if (ret) return ret; =20 @@ -730,14 +744,16 @@ static DEVICE_ATTR_WO(reset_buttons); =20 static void oxp_btn_queue_fn(struct work_struct *work) { + struct oxp_hid_cfg *cfg =3D container_of(to_delayed_work(work), + struct oxp_hid_cfg, oxp_btn_queue); int ret; =20 - if (READ_ONCE(drvdata.removing)) + if (READ_ONCE(cfg->removing)) return; =20 - ret =3D oxp_set_buttons(); + ret =3D oxp_set_buttons(cfg); if (ret) - dev_err(&drvdata.hdev->dev, + dev_err(&cfg->hdev->dev, "Error: Failed to write button mapping: %i\n", ret); } =20 @@ -756,6 +772,7 @@ static ssize_t map_button_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count, u8 index) { + struct oxp_hid_cfg *cfg =3D dev_get_drvdata(dev); int idx; =20 idx =3D oxp_button_idx_from_str(buf); @@ -764,64 +781,64 @@ static ssize_t map_button_store(struct device *dev, =20 switch (index) { case BUTTON_A: - drvdata.bmap_1->btn_a.mapping_idx =3D idx; + cfg->bmap_1->btn_a.mapping_idx =3D idx; break; case BUTTON_B: - drvdata.bmap_1->btn_b.mapping_idx =3D idx; + cfg->bmap_1->btn_b.mapping_idx =3D idx; break; case BUTTON_X: - drvdata.bmap_1->btn_x.mapping_idx =3D idx; + cfg->bmap_1->btn_x.mapping_idx =3D idx; break; case BUTTON_Y: - drvdata.bmap_1->btn_y.mapping_idx =3D idx; + cfg->bmap_1->btn_y.mapping_idx =3D idx; break; case BUTTON_LB: - drvdata.bmap_1->btn_lb.mapping_idx =3D idx; + cfg->bmap_1->btn_lb.mapping_idx =3D idx; break; case BUTTON_RB: - drvdata.bmap_1->btn_rb.mapping_idx =3D idx; + cfg->bmap_1->btn_rb.mapping_idx =3D idx; break; case BUTTON_LT: - drvdata.bmap_1->btn_lt.mapping_idx =3D idx; + cfg->bmap_1->btn_lt.mapping_idx =3D idx; break; case BUTTON_RT: - drvdata.bmap_1->btn_rt.mapping_idx =3D idx; + cfg->bmap_1->btn_rt.mapping_idx =3D idx; break; case BUTTON_START: - drvdata.bmap_1->btn_start.mapping_idx =3D idx; + cfg->bmap_1->btn_start.mapping_idx =3D idx; break; case BUTTON_SELECT: - drvdata.bmap_2->btn_select.mapping_idx =3D idx; + cfg->bmap_2->btn_select.mapping_idx =3D idx; break; case BUTTON_L3: - drvdata.bmap_2->btn_l3.mapping_idx =3D idx; + cfg->bmap_2->btn_l3.mapping_idx =3D idx; break; case BUTTON_R3: - drvdata.bmap_2->btn_r3.mapping_idx =3D idx; + cfg->bmap_2->btn_r3.mapping_idx =3D idx; break; case BUTTON_DUP: - drvdata.bmap_2->btn_dup.mapping_idx =3D idx; + cfg->bmap_2->btn_dup.mapping_idx =3D idx; break; case BUTTON_DDOWN: - drvdata.bmap_2->btn_ddown.mapping_idx =3D idx; + cfg->bmap_2->btn_ddown.mapping_idx =3D idx; break; case BUTTON_DLEFT: - drvdata.bmap_2->btn_dleft.mapping_idx =3D idx; + cfg->bmap_2->btn_dleft.mapping_idx =3D idx; break; case BUTTON_DRIGHT: - drvdata.bmap_2->btn_dright.mapping_idx =3D idx; + cfg->bmap_2->btn_dright.mapping_idx =3D idx; break; case BUTTON_M1: - drvdata.bmap_2->btn_m1.mapping_idx =3D idx; + cfg->bmap_2->btn_m1.mapping_idx =3D idx; break; case BUTTON_M2: - drvdata.bmap_2->btn_m2.mapping_idx =3D idx; + cfg->bmap_2->btn_m2.mapping_idx =3D idx; break; default: return -EINVAL; } - if (!READ_ONCE(drvdata.removing)) - mod_delayed_work(system_dfl_wq, &drvdata.oxp_btn_queue, + if (!READ_ONCE(cfg->removing)) + mod_delayed_work(system_dfl_wq, &cfg->oxp_btn_queue, msecs_to_jiffies(50)); return count; } @@ -830,62 +847,63 @@ static ssize_t map_button_show(struct device *dev, struct device_attribute *attr, char *buf, u8 index) { + struct oxp_hid_cfg *cfg =3D dev_get_drvdata(dev); u8 i; =20 switch (index) { case BUTTON_A: - i =3D drvdata.bmap_1->btn_a.mapping_idx; + i =3D cfg->bmap_1->btn_a.mapping_idx; break; case BUTTON_B: - i =3D drvdata.bmap_1->btn_b.mapping_idx; + i =3D cfg->bmap_1->btn_b.mapping_idx; break; case BUTTON_X: - i =3D drvdata.bmap_1->btn_x.mapping_idx; + i =3D cfg->bmap_1->btn_x.mapping_idx; break; case BUTTON_Y: - i =3D drvdata.bmap_1->btn_y.mapping_idx; + i =3D cfg->bmap_1->btn_y.mapping_idx; break; case BUTTON_LB: - i =3D drvdata.bmap_1->btn_lb.mapping_idx; + i =3D cfg->bmap_1->btn_lb.mapping_idx; break; case BUTTON_RB: - i =3D drvdata.bmap_1->btn_rb.mapping_idx; + i =3D cfg->bmap_1->btn_rb.mapping_idx; break; case BUTTON_LT: - i =3D drvdata.bmap_1->btn_lt.mapping_idx; + i =3D cfg->bmap_1->btn_lt.mapping_idx; break; case BUTTON_RT: - i =3D drvdata.bmap_1->btn_rt.mapping_idx; + i =3D cfg->bmap_1->btn_rt.mapping_idx; break; case BUTTON_START: - i =3D drvdata.bmap_1->btn_start.mapping_idx; + i =3D cfg->bmap_1->btn_start.mapping_idx; break; case BUTTON_SELECT: - i =3D drvdata.bmap_2->btn_select.mapping_idx; + i =3D cfg->bmap_2->btn_select.mapping_idx; break; case BUTTON_L3: - i =3D drvdata.bmap_2->btn_l3.mapping_idx; + i =3D cfg->bmap_2->btn_l3.mapping_idx; break; case BUTTON_R3: - i =3D drvdata.bmap_2->btn_r3.mapping_idx; + i =3D cfg->bmap_2->btn_r3.mapping_idx; break; case BUTTON_DUP: - i =3D drvdata.bmap_2->btn_dup.mapping_idx; + i =3D cfg->bmap_2->btn_dup.mapping_idx; break; case BUTTON_DDOWN: - i =3D drvdata.bmap_2->btn_ddown.mapping_idx; + i =3D cfg->bmap_2->btn_ddown.mapping_idx; break; case BUTTON_DLEFT: - i =3D drvdata.bmap_2->btn_dleft.mapping_idx; + i =3D cfg->bmap_2->btn_dleft.mapping_idx; break; case BUTTON_DRIGHT: - i =3D drvdata.bmap_2->btn_dright.mapping_idx; + i =3D cfg->bmap_2->btn_dright.mapping_idx; break; case BUTTON_M1: - i =3D drvdata.bmap_2->btn_m1.mapping_idx; + i =3D cfg->bmap_2->btn_m1.mapping_idx; break; case BUTTON_M2: - i =3D drvdata.bmap_2->btn_m2.mapping_idx; + i =3D cfg->bmap_2->btn_m2.mapping_idx; break; default: return -EINVAL; @@ -913,7 +931,7 @@ static ssize_t button_mapping_options_show(struct devic= e *dev, } static DEVICE_ATTR_RO(button_mapping_options); =20 -static int oxp_rumble_intensity_set(u8 intensity) +static int oxp_rumble_intensity_set(struct oxp_hid_cfg *cfg, u8 intensity) { u8 header[15] =3D { 0x02, 0x38, 0x02, 0xe3, 0x39, 0xe3, 0x39, 0xe3, 0x39, 0x01, intensity, 0x05, 0xe3, 0x39, 0xe3 }; @@ -926,13 +944,14 @@ static int oxp_rumble_intensity_set(u8 intensity) memcpy(data, header, header_size); memcpy(data + data_size - footer_size, footer, footer_size); =20 - return oxp_gen_2_property_out(OXP_FID_GEN2_RUMBLE_SET, data, data_size); + return oxp_gen_2_property_out(cfg, OXP_FID_GEN2_RUMBLE_SET, data, data_si= ze); } =20 static ssize_t rumble_intensity_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) { + struct oxp_hid_cfg *cfg =3D dev_get_drvdata(dev); int ret; u8 val; =20 @@ -943,11 +962,11 @@ static ssize_t rumble_intensity_store(struct device *= dev, if (val < 0 || val > 5) return -EINVAL; =20 - ret =3D oxp_rumble_intensity_set(val); + ret =3D oxp_rumble_intensity_set(cfg, val); if (ret) return ret; =20 - drvdata.rumble_intensity =3D val; + cfg->rumble_intensity =3D val; =20 return count; } @@ -955,7 +974,9 @@ static ssize_t rumble_intensity_store(struct device *de= v, static ssize_t rumble_intensity_show(struct device *dev, struct device_attribute *attr, char *buf) { - return sysfs_emit(buf, "%i\n", drvdata.rumble_intensity); + struct oxp_hid_cfg *cfg =3D dev_get_drvdata(dev); + + return sysfs_emit(buf, "%i\n", cfg->rumble_intensity); } static DEVICE_ATTR_RW(rumble_intensity); =20 @@ -1066,9 +1087,9 @@ static const struct attribute_group oxp_cfg_attrs_gro= up =3D { .attrs =3D oxp_cfg_attrs, }; =20 -static int oxp_rgb_status_store(u8 enabled, u8 speed, u8 brightness) +static int oxp_rgb_status_store(struct oxp_hid_cfg *cfg, u8 enabled, u8 sp= eed, u8 brightness) { - u16 up =3D get_usage_page(drvdata.hdev); + u16 up =3D get_usage_page(cfg->hdev); u8 *data; =20 /* Always default to max brightness and use intensity scaling when in @@ -1077,51 +1098,51 @@ static int oxp_rgb_status_store(u8 enabled, u8 spee= d, u8 brightness) switch (up) { case GEN1_USAGE_PAGE: data =3D (u8[4]) { OXP_SET_PROPERTY, enabled, speed, brightness }; - if (drvdata.rgb_effect =3D=3D OXP_EFFECT_MONO_LIST) + if (cfg->rgb_effect =3D=3D OXP_EFFECT_MONO_LIST) data[3] =3D 0x04; - return oxp_gen_1_property_out(OXP_FID_GEN1_RGB_SET, data, 4); + return oxp_gen_1_property_out(cfg, OXP_FID_GEN1_RGB_SET, data, 4); case GEN2_USAGE_PAGE: data =3D (u8[6]) { OXP_SET_PROPERTY, 0x00, 0x02, enabled, speed, brightn= ess }; - if (drvdata.rgb_effect =3D=3D OXP_EFFECT_MONO_LIST) + if (cfg->rgb_effect =3D=3D OXP_EFFECT_MONO_LIST) data[5] =3D 0x04; - return oxp_gen_2_property_out(OXP_FID_GEN2_STATUS_EVENT, data, 6); + return oxp_gen_2_property_out(cfg, OXP_FID_GEN2_STATUS_EVENT, data, 6); default: return -ENODEV; } } =20 -static ssize_t oxp_rgb_status_show(void) +static ssize_t oxp_rgb_status_show(struct oxp_hid_cfg *cfg) { - u16 up =3D get_usage_page(drvdata.hdev); + u16 up =3D get_usage_page(cfg->hdev); u8 *data; =20 - guard(mutex)(&drvdata.rgb_mutex); + guard(mutex)(&cfg->rgb_mutex); =20 switch (up) { case GEN1_USAGE_PAGE: data =3D (u8[1]) { OXP_GET_PROPERTY }; - return oxp_gen_1_property_out(OXP_FID_GEN1_RGB_SET, data, 1); + return oxp_gen_1_property_out(cfg, OXP_FID_GEN1_RGB_SET, data, 1); case GEN2_USAGE_PAGE: data =3D (u8[3]) { OXP_GET_PROPERTY, 0x00, 0x02 }; - return oxp_gen_2_property_out(OXP_FID_GEN2_STATUS_EVENT, data, 3); + return oxp_gen_2_property_out(cfg, OXP_FID_GEN2_STATUS_EVENT, data, 3); default: return -ENODEV; } } =20 -static int oxp_rgb_color_set(void) +static int oxp_rgb_color_set(struct oxp_hid_cfg *cfg) { - u8 br =3D drvdata.led_mc->led_cdev.brightness; - u16 up =3D get_usage_page(drvdata.hdev); + u8 br =3D cfg->led_mc->led_cdev.brightness; + u16 up =3D get_usage_page(cfg->hdev); u8 green, red, blue; size_t size; u8 *data; int i; =20 - led_mc_calc_color_components(drvdata.led_mc, br); - red =3D drvdata.led_mc->subled_info[0].brightness; - green =3D drvdata.led_mc->subled_info[1].brightness; - blue =3D drvdata.led_mc->subled_info[2].brightness; + led_mc_calc_color_components(cfg->led_mc, br); + red =3D cfg->led_mc->subled_info[0].brightness; + green =3D cfg->led_mc->subled_info[1].brightness; + blue =3D cfg->led_mc->subled_info[2].brightness; =20 switch (up) { case GEN1_USAGE_PAGE: @@ -1133,7 +1154,7 @@ static int oxp_rgb_color_set(void) data[3 * i + 2] =3D green; data[3 * i + 3] =3D blue; } - return oxp_gen_1_property_out(OXP_FID_GEN1_RGB_SET, data, size); + return oxp_gen_1_property_out(cfg, OXP_FID_GEN1_RGB_SET, data, size); case GEN2_USAGE_PAGE: size =3D 57; data =3D (u8[57]) { OXP_EFFECT_MONO_TRUE, 0x00, 0x02 }; @@ -1143,15 +1164,15 @@ static int oxp_rgb_color_set(void) data[3 * i + 1] =3D green; data[3 * i + 2] =3D blue; } - return oxp_gen_2_property_out(OXP_FID_GEN2_STATUS_EVENT, data, size); + return oxp_gen_2_property_out(cfg, OXP_FID_GEN2_STATUS_EVENT, data, size= ); default: return -ENODEV; } } =20 -static int oxp_rgb_effect_set(u8 effect) +static int oxp_rgb_effect_set(struct oxp_hid_cfg *cfg, u8 effect) { - u16 up =3D get_usage_page(drvdata.hdev); + u16 up =3D get_usage_page(cfg->hdev); u8 *data; int ret; =20 @@ -1178,18 +1199,18 @@ static int oxp_rgb_effect_set(u8 effect) switch (up) { case GEN1_USAGE_PAGE: data =3D (u8[1]) { effect }; - ret =3D oxp_gen_1_property_out(OXP_FID_GEN1_RGB_SET, data, 1); + ret =3D oxp_gen_1_property_out(cfg, OXP_FID_GEN1_RGB_SET, data, 1); break; case GEN2_USAGE_PAGE: data =3D (u8[3]) { effect, 0x00, 0x02 }; - ret =3D oxp_gen_2_property_out(OXP_FID_GEN2_STATUS_EVENT, data, 3); + ret =3D oxp_gen_2_property_out(cfg, OXP_FID_GEN2_STATUS_EVENT, data, 3); break; default: ret =3D -ENODEV; } break; case OXP_EFFECT_MONO_LIST: - ret =3D oxp_rgb_color_set(); + ret =3D oxp_rgb_color_set(cfg); break; default: return -EINVAL; @@ -1198,14 +1219,23 @@ static int oxp_rgb_effect_set(u8 effect) if (ret) return ret; =20 - drvdata.rgb_effect =3D effect; + cfg->rgb_effect =3D effect; =20 return 0; } =20 +static struct oxp_hid_cfg *oxp_rgb_cfg_from_dev(struct device *dev) +{ + struct led_classdev *led_cdev =3D dev_get_drvdata(dev); + struct led_classdev_mc *mc_cdev =3D lcdev_to_mccdev(led_cdev); + + return container_of(mc_cdev, struct oxp_hid_cfg, cdev); +} + static ssize_t enabled_store(struct device *dev, struct device_attribute *= attr, const char *buf, size_t count) { + struct oxp_hid_cfg *cfg =3D oxp_rgb_cfg_from_dev(dev); int ret; u8 val; =20 @@ -1214,30 +1244,31 @@ static ssize_t enabled_store(struct device *dev, st= ruct device_attribute *attr, return ret; val =3D ret; =20 - guard(mutex)(&drvdata.rgb_mutex); + guard(mutex)(&cfg->rgb_mutex); =20 - ret =3D oxp_rgb_status_store(val, drvdata.rgb_speed, - drvdata.rgb_brightness); + ret =3D oxp_rgb_status_store(cfg, val, cfg->rgb_speed, + cfg->rgb_brightness); if (ret) return ret; =20 - drvdata.rgb_en =3D val; + cfg->rgb_en =3D val; return count; } =20 static ssize_t enabled_show(struct device *dev, struct device_attribute *a= ttr, char *buf) { + struct oxp_hid_cfg *cfg =3D oxp_rgb_cfg_from_dev(dev); int ret; =20 - ret =3D oxp_rgb_status_show(); + ret =3D oxp_rgb_status_show(cfg); if (ret) return ret; =20 - if (drvdata.rgb_en >=3D ARRAY_SIZE(oxp_feature_en_text)) + if (cfg->rgb_en >=3D ARRAY_SIZE(oxp_feature_en_text)) return -EINVAL; =20 - return sysfs_emit(buf, "%s\n", oxp_feature_en_text[drvdata.rgb_en]); + return sysfs_emit(buf, "%s\n", oxp_feature_en_text[cfg->rgb_en]); } static DEVICE_ATTR_RW(enabled); =20 @@ -1260,6 +1291,7 @@ static DEVICE_ATTR_RO(enabled_index); static ssize_t effect_store(struct device *dev, struct device_attribute *a= ttr, const char *buf, size_t count) { + struct oxp_hid_cfg *cfg =3D oxp_rgb_cfg_from_dev(dev); u8 old_effect; int ret; u8 val; @@ -1270,20 +1302,20 @@ static ssize_t effect_store(struct device *dev, str= uct device_attribute *attr, =20 val =3D ret; =20 - guard(mutex)(&drvdata.rgb_mutex); - old_effect =3D drvdata.rgb_effect; - drvdata.rgb_effect =3D val; + guard(mutex)(&cfg->rgb_mutex); + old_effect =3D cfg->rgb_effect; + cfg->rgb_effect =3D val; =20 - ret =3D oxp_rgb_status_store(drvdata.rgb_en, drvdata.rgb_speed, - drvdata.rgb_brightness); + ret =3D oxp_rgb_status_store(cfg, cfg->rgb_en, cfg->rgb_speed, + cfg->rgb_brightness); if (ret) { - drvdata.rgb_effect =3D old_effect; + cfg->rgb_effect =3D old_effect; return ret; } =20 - ret =3D oxp_rgb_effect_set(val); + ret =3D oxp_rgb_effect_set(cfg, val); if (ret) { - drvdata.rgb_effect =3D old_effect; + cfg->rgb_effect =3D old_effect; return ret; } =20 @@ -1293,16 +1325,17 @@ static ssize_t effect_store(struct device *dev, str= uct device_attribute *attr, static ssize_t effect_show(struct device *dev, struct device_attribute *at= tr, char *buf) { + struct oxp_hid_cfg *cfg =3D oxp_rgb_cfg_from_dev(dev); int ret; =20 - ret =3D oxp_rgb_status_show(); + ret =3D oxp_rgb_status_show(cfg); if (ret) return ret; =20 - if (drvdata.rgb_effect >=3D ARRAY_SIZE(oxp_rgb_effect_text)) + if (cfg->rgb_effect >=3D ARRAY_SIZE(oxp_rgb_effect_text)) return -EINVAL; =20 - return sysfs_emit(buf, "%s\n", oxp_rgb_effect_text[drvdata.rgb_effect]); + return sysfs_emit(buf, "%s\n", oxp_rgb_effect_text[cfg->rgb_effect]); } =20 static DEVICE_ATTR_RW(effect); @@ -1326,6 +1359,7 @@ static DEVICE_ATTR_RO(effect_index); static ssize_t speed_store(struct device *dev, struct device_attribute *at= tr, const char *buf, size_t count) { + struct oxp_hid_cfg *cfg =3D oxp_rgb_cfg_from_dev(dev); int ret; u8 val; =20 @@ -1336,29 +1370,30 @@ static ssize_t speed_store(struct device *dev, stru= ct device_attribute *attr, if (val > 9) return -EINVAL; =20 - guard(mutex)(&drvdata.rgb_mutex); + guard(mutex)(&cfg->rgb_mutex); =20 - ret =3D oxp_rgb_status_store(drvdata.rgb_en, val, drvdata.rgb_brightness); + ret =3D oxp_rgb_status_store(cfg, cfg->rgb_en, val, cfg->rgb_brightness); if (ret) return ret; =20 - drvdata.rgb_speed =3D val; + cfg->rgb_speed =3D val; return count; } =20 static ssize_t speed_show(struct device *dev, struct device_attribute *att= r, char *buf) { + struct oxp_hid_cfg *cfg =3D oxp_rgb_cfg_from_dev(dev); int ret; =20 - ret =3D oxp_rgb_status_show(); + ret =3D oxp_rgb_status_show(cfg); if (ret) return ret; =20 - if (drvdata.rgb_speed > 9) + if (cfg->rgb_speed > 9) return -EINVAL; =20 - return sysfs_emit(buf, "%hhu\n", drvdata.rgb_speed); + return sysfs_emit(buf, "%hhu\n", cfg->rgb_speed); } static DEVICE_ATTR_RW(speed); =20 @@ -1371,42 +1406,47 @@ static DEVICE_ATTR_RO(speed_range); =20 static void oxp_rgb_queue_fn(struct work_struct *work) { - unsigned int max_brightness =3D drvdata.led_mc->led_cdev.max_brightness; - unsigned int brightness =3D drvdata.led_mc->led_cdev.brightness; + struct oxp_hid_cfg *cfg =3D container_of(to_delayed_work(work), + struct oxp_hid_cfg, oxp_rgb_queue); + unsigned int max_brightness =3D cfg->led_mc->led_cdev.max_brightness; + unsigned int brightness =3D cfg->led_mc->led_cdev.brightness; u8 val =3D 4 * brightness / max_brightness; int ret; =20 - if (READ_ONCE(drvdata.removing)) + if (READ_ONCE(cfg->removing)) return; =20 - guard(mutex)(&drvdata.rgb_mutex); + guard(mutex)(&cfg->rgb_mutex); =20 - if (drvdata.rgb_brightness !=3D val) { - ret =3D oxp_rgb_status_store(drvdata.rgb_en, drvdata.rgb_speed, val); + if (cfg->rgb_brightness !=3D val) { + ret =3D oxp_rgb_status_store(cfg, cfg->rgb_en, cfg->rgb_speed, val); if (ret) - dev_err(drvdata.led_mc->led_cdev.dev, + dev_err(cfg->led_mc->led_cdev.dev, "Error: Failed to write RGB Status: %i\n", ret); =20 - drvdata.rgb_brightness =3D val; + cfg->rgb_brightness =3D val; } =20 - if (drvdata.rgb_effect !=3D OXP_EFFECT_MONO_LIST) + if (cfg->rgb_effect !=3D OXP_EFFECT_MONO_LIST) return; =20 - ret =3D oxp_rgb_effect_set(drvdata.rgb_effect); + ret =3D oxp_rgb_effect_set(cfg, cfg->rgb_effect); if (ret) - dev_err(drvdata.led_mc->led_cdev.dev, "Error: Failed to write RGB color:= %i\n", + dev_err(cfg->led_mc->led_cdev.dev, "Error: Failed to write RGB color: %i= \n", ret); } =20 static void oxp_rgb_brightness_set(struct led_classdev *led_cdev, enum led_brightness brightness) { - if (READ_ONCE(drvdata.removing)) + struct led_classdev_mc *mc_cdev =3D lcdev_to_mccdev(led_cdev); + struct oxp_hid_cfg *cfg =3D container_of(mc_cdev, struct oxp_hid_cfg, cde= v); + + if (READ_ONCE(cfg->removing)) return; =20 led_cdev->brightness =3D brightness; - mod_delayed_work(system_dfl_wq, &drvdata.oxp_rgb_queue, msecs_to_jiffies(= 50)); + mod_delayed_work(system_dfl_wq, &cfg->oxp_rgb_queue, msecs_to_jiffies(50)= ); } =20 static struct attribute *oxp_rgb_attrs[] =3D { @@ -1423,7 +1463,7 @@ static const struct attribute_group oxp_rgb_attr_grou= p =3D { .attrs =3D oxp_rgb_attrs, }; =20 -static struct mc_subled oxp_rgb_subled_info[] =3D { +static const struct mc_subled oxp_rgb_subled_info[] =3D { { .color_index =3D LED_COLOR_ID_RED, .intensity =3D 0x24, @@ -1444,7 +1484,7 @@ static struct mc_subled oxp_rgb_subled_info[] =3D { }, }; =20 -static struct led_classdev_mc oxp_cdev_rgb =3D { +static const struct led_classdev_mc oxp_cdev_rgb =3D { .led_cdev =3D { .name =3D "oxp:rgb:joystick_rings", .color =3D LED_COLOR_ID_RGB, @@ -1453,7 +1493,6 @@ static struct led_classdev_mc oxp_cdev_rgb =3D { .brightness_set =3D oxp_rgb_brightness_set, }, .num_colors =3D ARRAY_SIZE(oxp_rgb_subled_info), - .subled_info =3D oxp_rgb_subled_info, }; =20 struct quirk_entry { @@ -1506,61 +1545,80 @@ static bool oxp_hybrid_mcu_device(void) return quirks->hybrid_mcu; } =20 -static void oxp_drain_output(void) +static void oxp_drain_output(struct oxp_hid_cfg *cfg) { /* Wait for any in-flight sysfs output before closing the transport. */ - guard(mutex)(&drvdata.cfg_mutex); + guard(mutex)(&cfg->cfg_mutex); } =20 -static void oxp_quiesce_work(void) +static void oxp_quiesce_work(struct oxp_hid_cfg *cfg) { - WRITE_ONCE(drvdata.removing, true); - if (drvdata.rgb_work_initialized) - disable_delayed_work_sync(&drvdata.oxp_rgb_queue); - if (drvdata.gen2_work_initialized) { - disable_delayed_work_sync(&drvdata.oxp_btn_queue); - disable_delayed_work_sync(&drvdata.oxp_mcu_init); + WRITE_ONCE(cfg->removing, true); + if (cfg->rgb_work_initialized) + disable_delayed_work_sync(&cfg->oxp_rgb_queue); + if (cfg->gen2_work_initialized) { + disable_delayed_work_sync(&cfg->oxp_btn_queue); + disable_delayed_work_sync(&cfg->oxp_mcu_init); } - oxp_drain_output(); + oxp_drain_output(cfg); +} + +static void oxp_cfg_release(void *data) +{ + struct oxp_hid_cfg *cfg =3D data; + + hid_set_drvdata(cfg->hdev, NULL); } =20 static int oxp_cfg_probe(struct hid_device *hdev, u16 up) { struct oxp_bmap_page_1 *bmap_1; struct oxp_bmap_page_2 *bmap_2; + struct oxp_hid_cfg *cfg; int ret; =20 - hid_set_drvdata(hdev, &drvdata); - mutex_init(&drvdata.cfg_mutex); - mutex_init(&drvdata.rgb_mutex); - drvdata.hdev =3D hdev; - drvdata.removing =3D false; + cfg =3D devm_kzalloc(&hdev->dev, sizeof(*cfg), GFP_KERNEL); + if (!cfg) + return -ENOMEM; + + cfg->hdev =3D hdev; + mutex_init(&cfg->cfg_mutex); + mutex_init(&cfg->rgb_mutex); + + /* Clear drvdata after registered callback objects have been released. */ + hid_set_drvdata(hdev, cfg); + ret =3D devm_add_action_or_reset(&hdev->dev, oxp_cfg_release, cfg); + if (ret) + return ret; =20 if (up =3D=3D GEN2_USAGE_PAGE && oxp_hybrid_mcu_device()) goto skip_rgb; =20 - drvdata.led_mc =3D &oxp_cdev_rgb; + cfg->cdev =3D oxp_cdev_rgb; + memcpy(cfg->subled_info, oxp_rgb_subled_info, sizeof(cfg->subled_info)); + cfg->cdev.subled_info =3D cfg->subled_info; + cfg->led_mc =3D &cfg->cdev; =20 - INIT_DELAYED_WORK(&drvdata.oxp_rgb_queue, oxp_rgb_queue_fn); - drvdata.rgb_work_initialized =3D true; - ret =3D devm_led_classdev_multicolor_register(&hdev->dev, &oxp_cdev_rgb); + INIT_DELAYED_WORK(&cfg->oxp_rgb_queue, oxp_rgb_queue_fn); + cfg->rgb_work_initialized =3D true; + ret =3D devm_led_classdev_multicolor_register(&hdev->dev, cfg->led_mc); if (ret) { dev_err_probe(&hdev->dev, ret, "Failed to create RGB device\n"); goto err_quiesce; } =20 - ret =3D devm_device_add_group(drvdata.led_mc->led_cdev.dev, + ret =3D devm_device_add_group(cfg->led_mc->led_cdev.dev, &oxp_rgb_attr_group); if (ret) { - dev_err_probe(drvdata.led_mc->led_cdev.dev, ret, - "Failed to create RGB configuration attributes\n"); + dev_err_probe(cfg->led_mc->led_cdev.dev, ret, + "Failed to create RGB configuration attributes\n"); goto err_quiesce; } =20 - ret =3D oxp_rgb_status_show(); + ret =3D oxp_rgb_status_show(cfg); if (ret) - dev_warn(drvdata.led_mc->led_cdev.dev, + dev_warn(cfg->led_mc->led_cdev.dev, "Failed to query RGB initial state: %i\n", ret); =20 /* Below features are only implemented in gen 2 */ @@ -1582,17 +1640,17 @@ skip_rgb: goto err_quiesce; } =20 - drvdata.bmap_1 =3D bmap_1; - drvdata.bmap_2 =3D bmap_2; - oxp_reset_buttons(); - INIT_DELAYED_WORK(&drvdata.oxp_btn_queue, oxp_btn_queue_fn); + cfg->bmap_1 =3D bmap_1; + cfg->bmap_2 =3D bmap_2; + oxp_reset_buttons(cfg); + INIT_DELAYED_WORK(&cfg->oxp_btn_queue, oxp_btn_queue_fn); =20 - drvdata.gamepad_mode =3D OXP_GP_MODE_XINPUT; - drvdata.rumble_intensity =3D 5; + cfg->gamepad_mode =3D OXP_GP_MODE_XINPUT; + cfg->rumble_intensity =3D 5; =20 - INIT_DELAYED_WORK(&drvdata.oxp_mcu_init, oxp_mcu_init_fn); - WRITE_ONCE(drvdata.gen2_work_initialized, true); - mod_delayed_work(system_dfl_wq, &drvdata.oxp_mcu_init, msecs_to_jiffies(5= 0)); + INIT_DELAYED_WORK(&cfg->oxp_mcu_init, oxp_mcu_init_fn); + WRITE_ONCE(cfg->gen2_work_initialized, true); + mod_delayed_work(system_dfl_wq, &cfg->oxp_mcu_init, msecs_to_jiffies(50)); =20 ret =3D devm_device_add_group(&hdev->dev, &oxp_cfg_attrs_group); if (ret) { @@ -1604,7 +1662,7 @@ skip_rgb: return 0; =20 err_quiesce: - oxp_quiesce_work(); + oxp_quiesce_work(cfg); return ret; } =20 @@ -1648,8 +1706,10 @@ static int oxp_hid_probe(struct hid_device *hdev, =20 static void oxp_hid_remove(struct hid_device *hdev) { - if (hid_get_drvdata(hdev)) - oxp_quiesce_work(); + struct oxp_hid_cfg *cfg =3D hid_get_drvdata(hdev); + + if (cfg) + oxp_quiesce_work(cfg); hid_hw_close(hdev); hid_hw_stop(hdev); } From nobody Fri Sep 25 17:42:22 2026 Received: from mail-oo2-f42.google.com (mail-oo2-f42.google.com [74.125.231.170]) (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 4AA3E3A48CA for ; Thu, 10 Sep 2026 03:21:38 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789010500; cv=none; b=NPY7sRrzxhYRiI54YPsPoMPev356U3ShkWeENhmDNy2NqTHPAX4QfEniCIXDJDdL3IthwGxfK/v528kk42xT+jJpWiGOjlo43HSNZFDio2pCiUJI+xU4OYyYfSjgKh5+kmVZsKRZDkzOIAWyZejQDeB7GR7QrgkriuicFlAHapo= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789010500; c=relaxed/simple; bh=LdBAAecAZ1OMl0W7f6TntsYbB4NODXa++UY4rORV7Es=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=Fg/CbFGWQIGixwJuTya7Ti0cHvbnPN7rLNYGZHbC3gqzR5xcIVbfgdoMx+1svH8c7DgZOretHXZXa8t6F7R/2L9CDbTDRXVzSIIf00bBguH00ufetz17LLax+OhkyaqYSKRRmjDHGWbG6lfs5OznCxMC30ydrZgAj2Jd8Nie1fE= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=XuzUXXcX; arc=none smtp.client-ip=74.125.231.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="XuzUXXcX" Received: by mail-oo2-f42.google.com with SMTP id 46e09a7af769-801d2f7a00eso164356a34.0 for ; Wed, 09 Sep 2026 20:21:38 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1789010497; x=1789615297; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=kyJeD3MenqHe8/v7HjjLsYU8Leth5wrcbz4y8p2ZViM=; b=XuzUXXcXxv3GM6VztOLRmyptG+gUUXvWz7PYGeisZ8ke/EoVl88nDDo5H6UBMaQyGl PxzWCb5/lPfsFkH7EVz/8uLBPrc/lvEWrYQGViVWr93LGvEk4YVmoTMEAyzSkZZ+XVvx viPKco7XvktUuNSmeR/9QjrN7up2ui8ibaBOVVuptoYJnpqBTCgyYOWHjYYB1/wdV72Z C18NJ3b61bJ2DzQejvhdMrm463ZzxackXoQ+96K6VJL8Njsx42gJ4TLGGHRfrxXhnf5T q7fGO7yU+W7uleM4J7C5atwBDfjatnWHyuSyFj9YL0eXaZ5u+dfkOH3Q1dgozf6weYsD ZkZw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1789010497; x=1789615297; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=kyJeD3MenqHe8/v7HjjLsYU8Leth5wrcbz4y8p2ZViM=; b=rNmXE7hL1wce/Tu5v033PXiuZcCXwr0zApbQUHl4Ti6LcW9Uq21CGzRsOnKu9pytQj BZnZ1nEcnuR4cY8YN/sz60BYX6StCgfxqrjR5AgZaSMSm5SeBHKEAWQQptlg18epEy/e UhCKda77V5GqkEBC1eeURiazePHdHi5tvPfiu2sIJK8IbDSvPWlbWAnZLeAJyXWWJHw/ 8wj8KLxErTHEusoqmtR3LlGikR50eVA6dFzmFJufrLST85gTwrO6TQ3+P8+RheICamCn 8ecVkRUA/GPePKOrAQOEG7sjN/X6IO5Qs8inccbpBH23WWjuN39kp0JL1YGv5Jpt7RZR P1SA== X-Forwarded-Encrypted: i=1; AKwUvBxxIjlFICuzdp8qtwV7Gxx2HfEcPzkhyokwf3HG8MhswmOshJZUIA9uNUdhQ2qMgU1Ab+LwUIN6P/4p5lk=@vger.kernel.org X-Gm-Message-State: AFuF++mhMbRI3/JMe2gpsHg6xiK76vL6OKDgHgWDw1AdRQKxhx/WVf9l VNzbPES7RW+DIO+EeFXY03xoZBNB6DokYarxTLNq6Ag/YT0GFcS19/zi X-Gm-Gg: AYBFou2ac1W5KSmCmNlfXUIsoPxxG+9uQ80QLAi4lgqMGg65RNEoSwe7vvOQ+VrcKIr dJD61kWgMHGbC8ptICmoqAhDrXziuomt2jUi7WofIhCqqrkub0Vc8ZtGCv2A0AQnJDDvgPu+iJN CoOlsWYrprXjiB1BYglCghzaF7BY7296WvvnCDClt2SX1ln0A8X4QVZnEwCYCFJxlenvVpB4kB6 G1tU3ntHXoe25AA2eKk/1Jri06JLj8iJkB/kGqqcBkz1LJf9wb/FsnO9mfR2hMDWeHop99BuLP4 rXCF49oChq5zuQ1f0MODzVK8jxbGdq4ddQYYRBhCbAnSfxGFG097rQ3p1Zd8P4ZYBk9D/tm+1Tz CYQ+u3kQyBzBhfbiYAvYAMvmmdRBpH1thy0L6VGJ4boilqx155rmhdTLovNifQh6mKxXX0Yk4IT 3MlxWyeGnArFTdBqGTw4rUy4Ps4s+qL0JjwwEI7G6hrKp9TtjGzx0yv690r61XyOR+VKHpS/K4j M7Tf1HDdd4MOQZC6JvCir2HQiV+toSjGt/URo2erEjia/Ig2iILS9I= X-Received: by 2002:a05:6830:91a:b0:7f4:5b0c:36a4 with SMTP id 46e09a7af769-7fb7be96d99mr19851635a34.3.1789010497206; Wed, 09 Sep 2026 20:21:37 -0700 (PDT) Received: from localhost ([76.187.192.44]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-7f9f6da9a49sm19758315a34.13.2026.09.09.20.21.36 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Wed, 09 Sep 2026 20:21:36 -0700 (PDT) From: Andrei Aldea To: Jiri Kosina , Benjamin Tissoires , "Derek J. Clark" Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Lee Jones , Pavel Machek , linux-leds@vger.kernel.org Subject: [PATCH 10/15] HID: hid-oxp: handle controller reinitialization across suspend Date: Wed, 9 Sep 2026 22:21:10 -0500 Message-ID: <20260910032115.28669-11-andrei1998@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260910032115.28669-1-andrei1998@gmail.com> References: <20260910032115.28669-1-andrei1998@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" A Gen2 monocolor reply uses the same command value as the asynchronous MCU reset notification. Treating every such report as a reset can schedule a spurious controller reinitialization. Track an outstanding monocolor write by command and zone under a per-HID spinlock, and consume its matching acknowledgment before considering the report a reset notification. Clear pending reply state during suspend and teardown. A missing reply does not change legacy transport success semantics. For system suspend, disable and drain initialized configuration work and reject new output while the device is suspended. Re-enable work on resume and queue a fallback reinitialization after the documented MCU reset interval. A qualifying reset notification can still bring that work forward. Leave runtime autosuspend unchanged. Fixes: 2f424f28fb39 ("HID: hid-oxp: Add Second Generation Gamepad Mode Swit= ch") Assisted-by: LLM Reviewed-by: Derek J. Clark Signed-off-by: Andrei Aldea Tested-by: Derek J. Clark --- drivers/hid/hid-oxp.c | 116 ++++++++++++++++++++++++++++++++++++++---- 1 file changed, 107 insertions(+), 9 deletions(-) diff --git a/drivers/hid/hid-oxp.c b/drivers/hid/hid-oxp.c index 7b36687..e26e6a9 100644 --- a/drivers/hid/hid-oxp.c +++ b/drivers/hid/hid-oxp.c @@ -16,6 +16,7 @@ #include #include #include +#include #include #include #include @@ -24,6 +25,7 @@ =20 #define OXP_PACKET_SIZE 64 #define OXP_STATUS_HEADER_SIZE 6 +#define OXP_STATUS_ACK 0x20 =20 #define GEN1_MESSAGE_ID 0xff #define GEN2_MESSAGE_ID 0x3f @@ -185,6 +187,10 @@ struct oxp_hid_cfg { struct hid_device *hdev; struct mutex cfg_mutex; /*ensure single synchronous output report*/ struct mutex rgb_mutex; /*serialize complete RGB transactions*/ + spinlock_t rgb_reply_lock; + u8 rgb_reply_command; + u8 rgb_reply_zone; + bool rgb_reply_pending; u8 rgb_brightness; u8 gamepad_mode; u8 rumble_intensity; @@ -193,6 +199,7 @@ struct oxp_hid_cfg { u8 rgb_en; bool rgb_work_initialized; bool gen2_work_initialized; + bool suspended; bool removing; }; =20 @@ -371,7 +378,7 @@ static void oxp_mcu_init_fn(struct work_struct *work) u8 gp_mode_data[3] =3D { OXP_GP_MODE_DEBUG, 0x01, 0x02 }; int ret; =20 - if (READ_ONCE(cfg->removing)) + if (READ_ONCE(cfg->suspended) || READ_ONCE(cfg->removing)) return; =20 /* Re-apply the button mapping */ @@ -410,6 +417,7 @@ static int oxp_hid_raw_event_gen_2(struct hid_device *h= dev, struct oxp_hid_cfg *cfg =3D hid_get_drvdata(hdev); struct led_classdev_mc *led_mc =3D cfg->led_mc; struct oxp_gen_2_rgb_report *rgb_rep; + bool solicited =3D false; =20 if (size < OXP_STATUS_HEADER_SIZE) return 0; @@ -417,11 +425,26 @@ static int oxp_hid_raw_event_gen_2(struct hid_device = *hdev, if (data[0] !=3D OXP_FID_GEN2_STATUS_EVENT) return 0; =20 + /* A monocolor acknowledgment is not an MCU reset notification. */ + if (data[5] =3D=3D OXP_STATUS_ACK) { + scoped_guard(spinlock_irqsave, &cfg->rgb_reply_lock) { + if (cfg->rgb_reply_pending && + data[3] =3D=3D cfg->rgb_reply_command && + data[4] =3D=3D cfg->rgb_reply_zone) { + cfg->rgb_reply_pending =3D false; + solicited =3D true; + } + } + if (solicited) + return 0; + } + /* Sent ~6s after resume event, indicating the MCU has fully reset. * Re-apply our settings after this has been received. */ if (data[3] =3D=3D OXP_EFFECT_MONO_TRUE) { if (READ_ONCE(cfg->gen2_work_initialized) && + !READ_ONCE(cfg->suspended) && !READ_ONCE(cfg->removing)) mod_delayed_work(system_dfl_wq, &cfg->oxp_mcu_init, msecs_to_jiffies(50)); @@ -489,6 +512,7 @@ static int mcu_property_out(struct oxp_hid_cfg *cfg, u8= *header, size_t header_s size_t data_size, u8 *footer, size_t footer_size) { unsigned char *dmabuf __free(kfree) =3D kzalloc(OXP_PACKET_SIZE, GFP_KERN= EL); + bool rgb_write; int ret; =20 if (!dmabuf) @@ -500,6 +524,19 @@ static int mcu_property_out(struct oxp_hid_cfg *cfg, u= 8 *header, size_t header_s guard(mutex)(&cfg->cfg_mutex); if (READ_ONCE(cfg->removing)) return -ENODEV; + if (READ_ONCE(cfg->suspended)) + return -EHOSTDOWN; + + rgb_write =3D header_size && data_size > 1 && + header[0] =3D=3D OXP_FID_GEN2_STATUS_EVENT && + data[0] =3D=3D OXP_EFFECT_MONO_TRUE; + if (rgb_write) { + scoped_guard(spinlock_irqsave, &cfg->rgb_reply_lock) { + cfg->rgb_reply_command =3D data[0]; + cfg->rgb_reply_zone =3D data[1]; + cfg->rgb_reply_pending =3D true; + } + } =20 memcpy(dmabuf, header, header_size); memcpy(dmabuf + header_size, data, data_size); @@ -509,12 +546,18 @@ static int mcu_property_out(struct oxp_hid_cfg *cfg, = u8 *header, size_t header_s dev_dbg(&cfg->hdev->dev, "raw data: [%*ph]\n", OXP_PACKET_SIZE, dmabuf); =20 ret =3D hid_hw_output_report(cfg->hdev, dmabuf, OXP_PACKET_SIZE); - if (ret < 0) - return ret; - /* MCU takes 200ms to be ready for another command. */ msleep(200); - return ret =3D=3D OXP_PACKET_SIZE ? 0 : -EIO; + if (ret >=3D 0) + ret =3D ret =3D=3D OXP_PACKET_SIZE ? 0 : -EIO; + + if (rgb_write) { + scoped_guard(spinlock_irqsave, &cfg->rgb_reply_lock) { + cfg->rgb_reply_pending =3D false; + } + } + + return ret; } =20 static int oxp_gen_1_property_out(struct oxp_hid_cfg *cfg, enum oxp_functi= on_index fid, u8 *data, @@ -748,7 +791,7 @@ static void oxp_btn_queue_fn(struct work_struct *work) struct oxp_hid_cfg, oxp_btn_queue); int ret; =20 - if (READ_ONCE(cfg->removing)) + if (READ_ONCE(cfg->suspended) || READ_ONCE(cfg->removing)) return; =20 ret =3D oxp_set_buttons(cfg); @@ -837,7 +880,7 @@ static ssize_t map_button_store(struct device *dev, default: return -EINVAL; } - if (!READ_ONCE(cfg->removing)) + if (!READ_ONCE(cfg->suspended) && !READ_ONCE(cfg->removing)) mod_delayed_work(system_dfl_wq, &cfg->oxp_btn_queue, msecs_to_jiffies(50)); return count; @@ -1413,7 +1456,7 @@ static void oxp_rgb_queue_fn(struct work_struct *work) u8 val =3D 4 * brightness / max_brightness; int ret; =20 - if (READ_ONCE(cfg->removing)) + if (READ_ONCE(cfg->suspended) || READ_ONCE(cfg->removing)) return; =20 guard(mutex)(&cfg->rgb_mutex); @@ -1442,7 +1485,7 @@ static void oxp_rgb_brightness_set(struct led_classde= v *led_cdev, struct led_classdev_mc *mc_cdev =3D lcdev_to_mccdev(led_cdev); struct oxp_hid_cfg *cfg =3D container_of(mc_cdev, struct oxp_hid_cfg, cde= v); =20 - if (READ_ONCE(cfg->removing)) + if (READ_ONCE(cfg->suspended) || READ_ONCE(cfg->removing)) return; =20 led_cdev->brightness =3D brightness; @@ -1554,6 +1597,9 @@ static void oxp_drain_output(struct oxp_hid_cfg *cfg) static void oxp_quiesce_work(struct oxp_hid_cfg *cfg) { WRITE_ONCE(cfg->removing, true); + scoped_guard(spinlock_irqsave, &cfg->rgb_reply_lock) { + cfg->rgb_reply_pending =3D false; + } if (cfg->rgb_work_initialized) disable_delayed_work_sync(&cfg->oxp_rgb_queue); if (cfg->gen2_work_initialized) { @@ -1584,6 +1630,7 @@ static int oxp_cfg_probe(struct hid_device *hdev, u16= up) cfg->hdev =3D hdev; mutex_init(&cfg->cfg_mutex); mutex_init(&cfg->rgb_mutex); + spin_lock_init(&cfg->rgb_reply_lock); =20 /* Clear drvdata after registered callback objects have been released. */ hid_set_drvdata(hdev, cfg); @@ -1714,6 +1761,54 @@ static void oxp_hid_remove(struct hid_device *hdev) hid_hw_stop(hdev); } =20 +static int __maybe_unused oxp_hid_suspend(struct hid_device *hdev, + pm_message_t message) +{ + struct oxp_hid_cfg *cfg =3D hid_get_drvdata(hdev); + + if (!cfg || PMSG_IS_AUTO(message)) + return 0; + + WRITE_ONCE(cfg->suspended, true); + scoped_guard(spinlock_irqsave, &cfg->rgb_reply_lock) { + cfg->rgb_reply_pending =3D false; + } + if (cfg->rgb_work_initialized) + disable_delayed_work_sync(&cfg->oxp_rgb_queue); + if (cfg->gen2_work_initialized) { + disable_delayed_work_sync(&cfg->oxp_btn_queue); + disable_delayed_work_sync(&cfg->oxp_mcu_init); + } + oxp_drain_output(cfg); + + return 0; +} + +static int __maybe_unused oxp_hid_resume(struct hid_device *hdev) +{ + struct oxp_hid_cfg *cfg =3D hid_get_drvdata(hdev); + + if (!cfg || !READ_ONCE(cfg->suspended) || + READ_ONCE(cfg->removing)) + return 0; + + if (cfg->rgb_work_initialized) + enable_delayed_work(&cfg->oxp_rgb_queue); + if (cfg->gen2_work_initialized) { + enable_delayed_work(&cfg->oxp_btn_queue); + enable_delayed_work(&cfg->oxp_mcu_init); + } + WRITE_ONCE(cfg->suspended, false); + if (!cfg->gen2_work_initialized) + return 0; + + /* Allow the controller MCU to finish rebooting before restoring state. */ + queue_delayed_work(system_dfl_wq, &cfg->oxp_mcu_init, + msecs_to_jiffies(6500)); + + return 0; +} + static const struct hid_device_id oxp_devices[] =3D { { HID_USB_DEVICE(USB_VENDOR_ID_CRSC, USB_DEVICE_ID_ONEXPLAYER_GEN1) }, { HID_USB_DEVICE(USB_VENDOR_ID_WCH, USB_DEVICE_ID_ONEXPLAYER_GEN2) }, @@ -1727,6 +1822,9 @@ static struct hid_driver hid_oxp =3D { .probe =3D oxp_hid_probe, .remove =3D oxp_hid_remove, .raw_event =3D oxp_hid_raw_event, + .suspend =3D pm_ptr(oxp_hid_suspend), + .resume =3D pm_ptr(oxp_hid_resume), + .reset_resume =3D pm_ptr(oxp_hid_resume), }; module_hid_driver(hid_oxp); From nobody Fri Sep 25 17:42:22 2026 Received: from mail-oo2-f42.google.com (mail-oo2-f42.google.com [74.125.231.170]) (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 574D73A6EF7 for ; Thu, 10 Sep 2026 03:21:39 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789010502; cv=none; b=sMgdZPvYfYh7sfF42eCsaNgtvUZYSB+R8YVVEzU9TNP5lPafKGY4E3G96tMvXyTPBWUGOzuaKnvA6exxGmLgoPEu9hmrQal7VAPmbiXzc21aGqObPuKblU7Kh5RQBpMHxVmWCgL73lKncK1LzaJZbKH4YKmQdRSa2NDT+qc6mHA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789010502; c=relaxed/simple; bh=GwMo0o8oCLVlyC7Dxi4sXtvc2pZog3douuyhlz9sz2U=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=aSGaQh2wAp8Yo7ubcH4ENu7evtI/p+JIUQ8/aUDpesrUBgKmdRaH5hPVtllK9h5vlhaAV4C/DHgbWyqzObhIwgoJO6FuavNAJ+FCXSQwE9UzIDrWmo0lZpAnZayiB2rtatJgEUbcKkp2nIthmTzAzw/jJu4cK1KtgRjGz6aMNNU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=aRTQ8pvh; arc=none smtp.client-ip=74.125.231.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="aRTQ8pvh" Received: by mail-oo2-f42.google.com with SMTP id 46e09a7af769-7f4bdf4f0e7so686411a34.3 for ; Wed, 09 Sep 2026 20:21:39 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1789010498; x=1789615298; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=J6MrMveQfRSLeZLxuVHTxBa8WIaiqwHHuULzMI/J9Yg=; b=aRTQ8pvhTJvg8VSzuwvlsFJPxfXLZYVgbKbY/t7vhnbbxUjpBKFF0XdtU6/WZh3gzv yNk4qW+LW1aZig6149NK/Wi1ythm87QSwPdUVKl7rbL8E/IfEMfVpCi694OZxBPA3H5/ nxvIled42jgPjS9BpcQdo4dIO3USb0usmWPCyixH8fXuE+oNxezTNGiE6GSjVdWNL25j IWpUYtg6q3NqhkVu9XmBqNFS9zCG78xs7hFgExHhR08JvJyFdZBgHk7fCSt9WwVM4Rln JaU39Z1dhDcr/ebjzUFkedCjJTouZ/EJfTuiWRdWPmX7Ecq4zJfcHbEyptekGH732+KH DzCw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1789010498; x=1789615298; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=J6MrMveQfRSLeZLxuVHTxBa8WIaiqwHHuULzMI/J9Yg=; b=XXY7tzRxvbSJe4i4AcojUdARYjrurDM41NvNMPysUGfZVBdbwkbnVxVs3u1eFaH6R5 stxGiuazjLcAPC26FMjNGg1esY/y9iOMRA9+vcdFgAW4FBFJjQI+gKfPscsLPEv4VRWX /XHX9Dhdg60/8BWT9vJL7sL2pjkFfSN2edI4jGSQKitrvfY3VInL59Rp3XPFcftv7745 Duo6ZGf79BVEkfho3F6jQ36y75gaqo6R3rGBvfCgjcJ8LZMQdT/Qs5S/ZReELxILYGkn NSQ/YIALU42uelFFPeScf0UhRSeXbJNq3xiH0sOS8BSnnjavAuwLBZx4XvOx+m4MoYY2 S56Q== X-Forwarded-Encrypted: i=1; AKwUvBx1rSXidNBnwymorXZra+rIQpEH2gkQ+CpArs14GXRPEPS1jgQF110wbm7cfxqErQKXM+1Cgc3mnkPet+Q=@vger.kernel.org X-Gm-Message-State: AFuF++nIqYPiv/2kyR//oVqU97fKuW7FNA/Uk4+6kK4/dRfRcv+yEoqU 2uaR6IPm7u/lfuN6Pum8rAXb1LBj0FhFcro3B+h1wsWL6W82nS3yfgxCsEevWuY877pGZPaG X-Gm-Gg: AYBFou2OaFlRi9DWa0h23rIU+M3PMAEWVK9ZhM7C9xpXp9H+NTyvYxCcJZMFFgvXn+N /4LPrrlzpj+KC9nb2M673AzUfZO4D7WRuWnMH50/20sC4k239s7dzrOU2omh0FfQuz7hCRE6NVM kKnDVr5vEcI8QwXu975BrHFU+DvCtTQU1+NkxOx3CtRrgXa6e13J1SYJQ66LL/DIv12dDl3OYa9 /sH+B0cid4la53rrhxiJwTR48HigpjQ87x6MsRxyUfwV8mRD4NsRMvNjyMpiRx3JNWN+SZlu0Au sX+p3RPZQPNG2MlLziyHIvNXMTnpaywy7MtvASDANGPvsFDiaBMT7llfKBocqcKrY1vp5GGGpIG isTXnbX0pppY9druiq2R/j9BVA8IP7FJUqzTYbH6uGZicY4HoKDsbB+hu2Q3WqBju2G/107VfeP dCC4AF4Pd5YAzEveyhBQEPBvr1tCrsoq5af3qd/dT34ctvqq2v2/7I6Q6N/yMNS1p4pCq70qg8L IaieaGYBYisjnizZPupaYzeAENOYzUYWZPxWowy0voSNo+CtfTv3EQ= X-Received: by 2002:a05:6830:82aa:b0:7e9:e1ca:5316 with SMTP id 46e09a7af769-7fb76b4310dmr15524161a34.0.1789010498167; Wed, 09 Sep 2026 20:21:38 -0700 (PDT) Received: from localhost ([76.187.192.44]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-7f9f6da9a49sm19758315a34.13.2026.09.09.20.21.37 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Wed, 09 Sep 2026 20:21:37 -0700 (PDT) From: Andrei Aldea To: Jiri Kosina , Benjamin Tissoires , "Derek J. Clark" Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Lee Jones , Pavel Machek , linux-leds@vger.kernel.org Subject: [PATCH 11/15] HID: hid-oxp: group declarations and protocol definitions Date: Wed, 9 Sep 2026 22:21:11 -0500 Message-ID: <20260910032115.28669-12-andrei1998@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260910032115.28669-1-andrei1998@gmail.com> References: <20260910032115.28669-1-andrei1998@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Collect protocol definitions and helper macros at the top of the file, place the quirk structure with the other type declarations, and tidy configuration field ordering and callback declaration formatting. Keep this cleanup separate from the preceding behavioral fixes and the new-controller feature patches. Assisted-by: LLM Reviewed-by: Derek J. Clark Signed-off-by: Andrei Aldea Tested-by: Derek J. Clark --- drivers/hid/hid-oxp.c | 116 ++++++++++++++++++++++-------------------- 1 file changed, 60 insertions(+), 56 deletions(-) diff --git a/drivers/hid/hid-oxp.c b/drivers/hid/hid-oxp.c index e26e6a9..8c44f57 100644 --- a/drivers/hid/hid-oxp.c +++ b/drivers/hid/hid-oxp.c @@ -33,6 +33,30 @@ #define GEN1_USAGE_PAGE 0xff01 #define GEN2_USAGE_PAGE 0xff00 =20 +#define OXP_MAPPING_GAMEPAD 0x01 +#define OXP_MAPPING_KEYBOARD 0x02 +#define OXP_FILL_PAGE_SLOT(page, btn) \ + { .button_idx =3D (page)->btn.button_idx, \ + .mapping_idx =3D (page)->btn.mapping_idx } + +#define OXP_GET_PROPERTY 0xfc +#define OXP_SET_PROPERTY 0xfd +#define OXP_EFFECT_MONO_TRUE 0xfe /* actual index for monocolor */ + +#define OXP_DEVICE_ATTR_RW(_name, _group) = \ + static ssize_t _name##_store(struct device *dev, \ + struct device_attribute *attr, \ + const char *buf, size_t count) \ + { \ + return _group##_store(dev, attr, buf, count, _name.index); \ + } \ + static ssize_t _name##_show(struct device *dev, \ + struct device_attribute *attr, char *buf) \ + { \ + return _group##_show(dev, attr, buf, _name.index); \ + } \ + static DEVICE_ATTR_RW(_name) + enum oxp_function_index { OXP_FID_GEN1_RGB_SET =3D 0x07, OXP_FID_GEN1_RGB_REPLY =3D 0x0f, @@ -42,9 +66,6 @@ enum oxp_function_index { OXP_FID_GEN2_STATUS_EVENT =3D 0xb8, }; =20 -#define OXP_MAPPING_GAMEPAD 0x01 -#define OXP_MAPPING_KEYBOARD 0x02 - struct oxp_button_data { u8 mode; u8 index; @@ -176,37 +197,38 @@ struct oxp_bmap_page_2 { =20 /* Hybrid devices expose RGB and controller configuration on separate HIDs= . */ struct oxp_hid_cfg { - struct led_classdev_mc cdev; - struct mc_subled subled_info[3]; - struct delayed_work oxp_rgb_queue; + /* General HID state */ + struct hid_device *hdev; + struct mutex cfg_mutex; /*ensure single synchronous output report*/ + bool suspended; + bool removing; + + /* Gamepad state */ struct delayed_work oxp_btn_queue; + struct delayed_work oxp_mcu_init; struct oxp_bmap_page_1 *bmap_1; struct oxp_bmap_page_2 *bmap_2; - struct delayed_work oxp_mcu_init; + bool gen2_work_initialized; + u8 rumble_intensity; + u8 gamepad_mode; + + /* RGB state */ + struct delayed_work oxp_rgb_queue; + struct mc_subled subled_info[3]; struct led_classdev_mc *led_mc; - struct hid_device *hdev; - struct mutex cfg_mutex; /*ensure single synchronous output report*/ - struct mutex rgb_mutex; /*serialize complete RGB transactions*/ + struct led_classdev_mc cdev; spinlock_t rgb_reply_lock; + struct mutex rgb_mutex; /*serialize complete RGB transactions*/ + bool rgb_work_initialized; + bool rgb_reply_pending; u8 rgb_reply_command; u8 rgb_reply_zone; - bool rgb_reply_pending; u8 rgb_brightness; - u8 gamepad_mode; - u8 rumble_intensity; u8 rgb_effect; u8 rgb_speed; u8 rgb_en; - bool rgb_work_initialized; - bool gen2_work_initialized; - bool suspended; - bool removing; }; =20 -#define OXP_FILL_PAGE_SLOT(page, btn) \ - { .button_idx =3D (page)->btn.button_idx, \ - .mapping_idx =3D (page)->btn.mapping_idx } - enum oxp_gamepad_mode_index { OXP_GP_MODE_XINPUT =3D 0x00, OXP_GP_MODE_DEBUG =3D 0x03, @@ -251,14 +273,6 @@ enum oxp_rgb_effect_index { OXP_EFFECT_MONO_LIST, /* placeholder for effect_index_show */ }; =20 -/* These belong to rgb_effect_index, but we want to hide them from - * rgb_effect_text - */ - -#define OXP_GET_PROPERTY 0xfc -#define OXP_SET_PROPERTY 0xfd -#define OXP_EFFECT_MONO_TRUE 0xfe /* actual index for monocolor */ - static const char *const oxp_rgb_effect_text[] =3D { [OXP_UNKNOWN] =3D "unknown", [OXP_EFFECT_AURORA] =3D "aurora", @@ -321,6 +335,10 @@ struct oxp_attr { u8 index; }; =20 +struct quirk_entry { + bool hybrid_mcu; +}; + static u16 get_usage_page(struct hid_device *hdev) { return hdev->collection[0].usage >> 16; @@ -508,8 +526,9 @@ static int oxp_hid_raw_event(struct hid_device *hdev, s= truct hid_report *report, return 0; } =20 -static int mcu_property_out(struct oxp_hid_cfg *cfg, u8 *header, size_t he= ader_size, u8 *data, - size_t data_size, u8 *footer, size_t footer_size) +static int mcu_property_out(struct oxp_hid_cfg *cfg, u8 *header, + size_t header_size, u8 *data, size_t data_size, + u8 *footer, size_t footer_size) { unsigned char *dmabuf __free(kfree) =3D kzalloc(OXP_PACKET_SIZE, GFP_KERN= EL); bool rgb_write; @@ -560,7 +579,8 @@ static int mcu_property_out(struct oxp_hid_cfg *cfg, u8= *header, size_t header_s return ret; } =20 -static int oxp_gen_1_property_out(struct oxp_hid_cfg *cfg, enum oxp_functi= on_index fid, u8 *data, +static int oxp_gen_1_property_out(struct oxp_hid_cfg *cfg, + enum oxp_function_index fid, u8 *data, u8 data_size) { u8 header[] =3D { fid, GEN1_MESSAGE_ID }; @@ -569,7 +589,8 @@ static int oxp_gen_1_property_out(struct oxp_hid_cfg *c= fg, enum oxp_function_ind return mcu_property_out(cfg, header, header_size, data, data_size, NULL, = 0); } =20 -static int oxp_gen_2_property_out(struct oxp_hid_cfg *cfg, enum oxp_functi= on_index fid, u8 *data, +static int oxp_gen_2_property_out(struct oxp_hid_cfg *cfg, + enum oxp_function_index fid, u8 *data, u8 data_size) { u8 header[] =3D { fid, GEN2_MESSAGE_ID, 0x01 }; @@ -1030,20 +1051,6 @@ static ssize_t rumble_intensity_range_show(struct de= vice *dev, } static DEVICE_ATTR_RO(rumble_intensity_range); =20 -#define OXP_DEVICE_ATTR_RW(_name, _group) = \ - static ssize_t _name##_store(struct device *dev, \ - struct device_attribute *attr, \ - const char *buf, size_t count) \ - { \ - return _group##_store(dev, attr, buf, count, _name.index); \ - } \ - static ssize_t _name##_show(struct device *dev, \ - struct device_attribute *attr, char *buf) \ - { \ - return _group##_show(dev, attr, buf, _name.index); \ - } \ - static DEVICE_ATTR_RW(_name) - static struct oxp_attr button_a =3D { BUTTON_A }; OXP_DEVICE_ATTR_RW(button_a, map_button); =20 @@ -1130,7 +1137,8 @@ static const struct attribute_group oxp_cfg_attrs_gro= up =3D { .attrs =3D oxp_cfg_attrs, }; =20 -static int oxp_rgb_status_store(struct oxp_hid_cfg *cfg, u8 enabled, u8 sp= eed, u8 brightness) +static int oxp_rgb_status_store(struct oxp_hid_cfg *cfg, u8 enabled, + u8 speed, u8 brightness) { u16 up =3D get_usage_page(cfg->hdev); u8 *data; @@ -1538,10 +1546,6 @@ static const struct led_classdev_mc oxp_cdev_rgb =3D= { .num_colors =3D ARRAY_SIZE(oxp_rgb_subled_info), }; =20 -struct quirk_entry { - bool hybrid_mcu; -}; - static struct quirk_entry quirk_hybrid_mcu =3D { .hybrid_mcu =3D true, }; @@ -1651,7 +1655,7 @@ static int oxp_cfg_probe(struct hid_device *hdev, u16= up) ret =3D devm_led_classdev_multicolor_register(&hdev->dev, cfg->led_mc); if (ret) { dev_err_probe(&hdev->dev, ret, - "Failed to create RGB device\n"); + "Failed to create RGB device\n"); goto err_quiesce; } =20 @@ -1676,14 +1680,14 @@ skip_rgb: bmap_1 =3D devm_kzalloc(&hdev->dev, sizeof(struct oxp_bmap_page_1), GFP_K= ERNEL); if (!bmap_1) { ret =3D dev_err_probe(&hdev->dev, -ENOMEM, - "Unable to allocate button map page 1\n"); + "Unable to allocate button map page 1\n"); goto err_quiesce; } =20 bmap_2 =3D devm_kzalloc(&hdev->dev, sizeof(struct oxp_bmap_page_2), GFP_K= ERNEL); if (!bmap_2) { ret =3D dev_err_probe(&hdev->dev, -ENOMEM, - "Unable to allocate button map page 2\n"); + "Unable to allocate button map page 2\n"); goto err_quiesce; } =20 @@ -1702,7 +1706,7 @@ skip_rgb: ret =3D devm_device_add_group(&hdev->dev, &oxp_cfg_attrs_group); if (ret) { dev_err_probe(&hdev->dev, ret, - "Failed to attach configuration attributes\n"); + "Failed to attach configuration attributes\n"); goto err_quiesce; } From nobody Fri Sep 25 17:42:22 2026 Received: from mail-oo2-f42.google.com (mail-oo2-f42.google.com [74.125.231.170]) (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 4DCE33A7F52 for ; Thu, 10 Sep 2026 03:21:40 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789010502; cv=none; b=jOYFC6cMvwCbE1k6Rt2LDy+FquQC3y0ygD/rp28ozFFFE+FtHhdUSeLUTnSzRDM94K6dyR70ADM/SHLkj2torhZKIsqE+0206NY1mBvOHocKTy/wy+bftsk48vofNNQ+VSDCseU01QRcl1GZrRjgJWfbuXvOSVvq6W7cEcUX4Vk= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789010502; c=relaxed/simple; bh=AeLnTpy9Imgwdp3HIzHn/henHDdgDKkOfb55UxqoU0k=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=oQH0Q4+PKmcYKIGC6HcGEBgIy4dhX4xKPtRL67FVhYTsda0sWf2Uycy5EQFGAGQgfQa1N+1/6iNKQgd3wSHJfKvtW1FqIlwvW4Tf0Pb0Ucc0+OEvJIGZ/f4fImSTh36D8v9hWastYh/rf1IYjKzGR07yISHwSwGT+UYbite/QFo= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=GLj+z0me; arc=none smtp.client-ip=74.125.231.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="GLj+z0me" Received: by mail-oo2-f42.google.com with SMTP id 46e09a7af769-7f4bdf4f0e9so697756a34.2 for ; Wed, 09 Sep 2026 20:21:40 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1789010499; x=1789615299; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=SgtfPs4JGN+ren3NYbKyWAaqrzZ1zJ4bq29kq6OscHA=; b=GLj+z0mehQBKa3TMx2Zmj5d7Ej9p+vnFaLzGUwAUTpylHm22EYr5FrcLQrjE443Mxo 5i2qKnatpN4qJTp3Og8GkXW0DoPyfrX9DsfMLGRbHSQUWixgPCapfpgGIhZ9zxEG81t2 SgY8rEpSqyGYbx4Qu4nQ/rGlAsB5fqNgxsEEX9Us2/QkhkvX17l8HIDF5V25OCrTmQaf mncl5o1FYsnYnPUdlX3CrC+YadHSQiVeFGDQxZeEcXk5ry5vAMCVMnVZm/1Nu7xvwuKW q4GHGsAXFxTRhHkGzen+/+gw1gVRkJlmV1gko5tTPTs5bdakoOLCdrhB8cX+95vqWILI L/XQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1789010499; x=1789615299; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=SgtfPs4JGN+ren3NYbKyWAaqrzZ1zJ4bq29kq6OscHA=; b=BrQUDBlniD4vZnyrO8gJw3+UJhCbFMOLqXQPcWyExwrRT1+73lCPyGntejbXokbP7w 1xtg9Cw9m/a65hbleDkL/3qo2bXAZ5JHt38PTasyi29I4q2h6z0LkkoDfw8gNe6NcUEP jqrLg4dOuaFguNiJCEmzmk/w1pJLbMGCwX8QxjP3HqxMp+Ri9WUV2ju93JSicPXBR1AZ 4aZhIFWyPn0Cw2zbylZGDjzdnONEMNgSvKW2F/7FBq5gZnLtwszhNvr0J74Tqvt1jTh+ Spt4hLPmZpm81GuT83M3tpK2EPMNHe3dUDwY5AzPZI1/bDxkqvq4CC6M27XvGZal/Ecp efxA== X-Forwarded-Encrypted: i=1; AKwUvBwyj6sNcbhKmcqr/NScBrzPtUsWJ9gJ1GmoI/Tp2D3Y5rDUNwAF9A++0bRbPQ9WSi3OKCxegBbtp26oBNk=@vger.kernel.org X-Gm-Message-State: AFuF++nVUY36LXDJMaMA9YR4ohlzMSNDLRFMJFvLqmo/LnUMrHCV4sby g/bLMN+eANomqmMrbB4wHQt2ouZk++5Rx217T+Vi5Tbk8ZFuahlTW28Y X-Gm-Gg: AYBFou0g2w71/l7VY0KIOL3tVSgnFwj4gAeu+tNOxWsEA6Gr3Cwger3BNyCAV1NiPzW Vb/Ekzyrj2CWNSSNpuV/+1O7uC6by8ibFDt07LtEW/KTWrLfa4rK0QYUk3BMkSGhjlaLnsFjm+L /nJ3Dmg4P4skFtpYm5C2SDY3M2s8P9IpyURJdIFLy0xQejcH2URUAIJJTyWqiYUX3tXob3MVs55 WQBcPn9o6iyxoAi6HeN3Ifv73QhoW6No0zakSbadC966DTrwMVdcqMf3Mo/q0bEZxeEfIuz2o0C HA7sd6KDYr1wMrxnB96fpwSSd8lIWlhSl617KrycKfmHvHmCuLy5EzkPY9T/ggTqwBBbpOQkEcK adCiYwJAH2XnrDzlvaVU7P4Ao9mjJkRYIYPbBudiyFfrFHmMiy1kT4F1YDco7rf0Hu53S5Ra+Am ZtwCe3bOcrGILGVqa58LtZD6lKHv2Syj8nNdP/IkdDlePo5LWN9vjq6VGnlgjKmggkiLnzCO2mQ 5YH21CWhA4brc74qfLU5g98KYJbHa/BZOi1laQTASz9YT6COjdngDLm+g== X-Received: by 2002:a05:6830:a1c8:20b0:800:92d0:df8c with SMTP id 46e09a7af769-80092d0e1damr5057405a34.1.1789010499237; Wed, 09 Sep 2026 20:21:39 -0700 (PDT) Received: from localhost ([76.187.192.44]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-7f9f6da9a49sm19758315a34.13.2026.09.09.20.21.38 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Wed, 09 Sep 2026 20:21:38 -0700 (PDT) From: Andrei Aldea To: Jiri Kosina , Benjamin Tissoires , "Derek J. Clark" Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 12/15] HID: hid-oxp: support three-page button maps on X2 controllers Date: Wed, 9 Sep 2026 22:21:12 -0500 Message-ID: <20260910032115.28669-13-andrei1998@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260910032115.28669-1-andrei1998@gmail.com> References: <20260910032115.28669-1-andrei1998@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" The ONEXPLAYER 3 and X2 Mini Pro need mapping format 0x02 and a third page preserving the extra buttons' factory mappings. Use exact DMI matches and select configuration interface 2; the other HID interfaces remain available without duplicate configuration or LED registration. Initialize each page with the selected format. Page three is fixed because its factory encodings have no entries in the public mapping table. Legacy devices keep their two-page format 0x20 transaction. Fixes: e4c850a6e750 ("HID: hid-oxp: Add Button Mapping Interface") Assisted-by: LLM Reviewed-by: Derek J. Clark Signed-off-by: Andrei Aldea --- drivers/hid/hid-oxp.c | 94 ++++++++++++++++++++++++++++++++++++------- 1 file changed, 79 insertions(+), 15 deletions(-) diff --git a/drivers/hid/hid-oxp.c b/drivers/hid/hid-oxp.c index 8c44f57..48fa916 100644 --- a/drivers/hid/hid-oxp.c +++ b/drivers/hid/hid-oxp.c @@ -19,6 +19,7 @@ #include #include #include +#include #include =20 #include "hid-ids.h" @@ -35,6 +36,8 @@ =20 #define OXP_MAPPING_GAMEPAD 0x01 #define OXP_MAPPING_KEYBOARD 0x02 +#define OXP_BMAP_FORMAT_DEFAULT 0x20 +#define OXP_BMAP_FORMAT_X2 0x02 #define OXP_FILL_PAGE_SLOT(page, btn) \ { .button_idx =3D (page)->btn.button_idx, \ .mapping_idx =3D (page)->btn.mapping_idx } @@ -159,9 +162,9 @@ enum oxp_joybutton_index { BUTTON_DRIGHT, BUTTON_M1 =3D 0x22, BUTTON_M2, - /* These are unused currently, reserved for future devices */ BUTTON_M3, BUTTON_M4, + /* These are unused currently, reserved for future devices */ BUTTON_M5, BUTTON_M6, }; @@ -209,8 +212,10 @@ struct oxp_hid_cfg { struct oxp_bmap_page_1 *bmap_1; struct oxp_bmap_page_2 *bmap_2; bool gen2_work_initialized; + bool bmap_page_3; u8 rumble_intensity; u8 gamepad_mode; + u8 bmap_format; =20 /* RGB state */ struct delayed_work oxp_rgb_queue; @@ -337,6 +342,9 @@ struct oxp_attr { =20 struct quirk_entry { bool hybrid_mcu; + bool bmap_page_3; + u8 cfg_interface_num; + u8 bmap_format; }; =20 static u16 get_usage_page(struct hid_device *hdev) @@ -735,8 +743,17 @@ static void oxp_page_fill_data(char *buf, const struct= oxp_button_idx *buttons, =20 static int oxp_set_buttons(struct oxp_hid_cfg *cfg) { - u8 page_1[59] =3D { 0x02, 0x38, 0x20, 0x01, 0x01 }; - u8 page_2[59] =3D { 0x02, 0x38, 0x20, 0x02, 0x01 }; + u8 page_1[59] =3D { 0x02, 0x38, cfg->bmap_format, 0x01, 0x01 }; + u8 page_2[59] =3D { 0x02, 0x38, cfg->bmap_format, 0x02, 0x01 }; + u8 page_3[59] =3D { + 0x02, 0x38, cfg->bmap_format, 0x03, 0x01, + /* + * M3/M4 have no mutable sysfs mapping slots. Keep their factory + * encodings, which are not entries in oxp_button_table. + */ + BUTTON_M3, OXP_MAPPING_KEYBOARD, 0x02, 0x05, 0x00, 0x00, + BUTTON_M4, OXP_MAPPING_GAMEPAD, 0x21, 0x00, 0x00, 0x00, + }; u16 up =3D get_usage_page(cfg->hdev); int ret; =20 @@ -774,7 +791,11 @@ static int oxp_set_buttons(struct oxp_hid_cfg *cfg) if (ret) return ret; =20 - return oxp_gen_2_property_out(cfg, OXP_FID_GEN2_KEY_STATE, page_2, ARRAY_= SIZE(page_2)); + ret =3D oxp_gen_2_property_out(cfg, OXP_FID_GEN2_KEY_STATE, page_2, ARRAY= _SIZE(page_2)); + if (ret || !cfg->bmap_page_3) + return ret; + + return oxp_gen_2_property_out(cfg, OXP_FID_GEN2_KEY_STATE, page_3, ARRAY_= SIZE(page_3)); } =20 static void oxp_reset_buttons(struct oxp_hid_cfg *cfg) @@ -1550,7 +1571,13 @@ static struct quirk_entry quirk_hybrid_mcu =3D { .hybrid_mcu =3D true, }; =20 -static const struct dmi_system_id oxp_hybrid_mcu_list[] =3D { +static struct quirk_entry quirk_x2_bmap =3D { + .bmap_format =3D OXP_BMAP_FORMAT_X2, + .bmap_page_3 =3D true, + .cfg_interface_num =3D 2, +}; + +static const struct dmi_system_id oxp_quirk_list[] =3D { { .ident =3D "OneXPlayer Apex", .matches =3D { @@ -1575,21 +1602,34 @@ static const struct dmi_system_id oxp_hybrid_mcu_li= st[] =3D { }, .driver_data =3D &quirk_hybrid_mcu, }, + { + .ident =3D "OneXPlayer 3", + .matches =3D { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ONE-NETBOOK"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ONEXPLAYER 3"), + }, + .driver_data =3D &quirk_x2_bmap, + }, + { + .ident =3D "OneXPlayer X2 Mini Pro", + .matches =3D { + DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ONE-NETBOOK"), + DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ONEXPLAYER X2Mini PRO"), + }, + .driver_data =3D &quirk_x2_bmap, + }, {}, }; =20 -static bool oxp_hybrid_mcu_device(void) +static const struct quirk_entry *oxp_get_quirks(void) { const struct dmi_system_id *dmi_id; - struct quirk_entry *quirks; =20 - dmi_id =3D dmi_first_match(oxp_hybrid_mcu_list); + dmi_id =3D dmi_first_match(oxp_quirk_list); if (!dmi_id) - return false; + return NULL; =20 - quirks =3D dmi_id->driver_data; - - return quirks->hybrid_mcu; + return dmi_id->driver_data; } =20 static void oxp_drain_output(struct oxp_hid_cfg *cfg) @@ -1613,6 +1653,21 @@ static void oxp_quiesce_work(struct oxp_hid_cfg *cfg) oxp_drain_output(cfg); } =20 +static bool oxp_is_cfg_interface(struct hid_device *hdev, + const struct quirk_entry *quirks) +{ + struct usb_interface *intf; + + if (!quirks || !quirks->cfg_interface_num) + return true; + if (hdev->bus !=3D BUS_USB) + return false; + + intf =3D to_usb_interface(hdev->dev.parent); + return intf->cur_altsetting->desc.bInterfaceNumber =3D=3D + quirks->cfg_interface_num; +} + static void oxp_cfg_release(void *data) { struct oxp_hid_cfg *cfg =3D data; @@ -1620,7 +1675,8 @@ static void oxp_cfg_release(void *data) hid_set_drvdata(cfg->hdev, NULL); } =20 -static int oxp_cfg_probe(struct hid_device *hdev, u16 up) +static int oxp_cfg_probe(struct hid_device *hdev, u16 up, + const struct quirk_entry *quirks) { struct oxp_bmap_page_1 *bmap_1; struct oxp_bmap_page_2 *bmap_2; @@ -1642,7 +1698,7 @@ static int oxp_cfg_probe(struct hid_device *hdev, u16= up) if (ret) return ret; =20 - if (up =3D=3D GEN2_USAGE_PAGE && oxp_hybrid_mcu_device()) + if (up =3D=3D GEN2_USAGE_PAGE && quirks && quirks->hybrid_mcu) goto skip_rgb; =20 cfg->cdev =3D oxp_cdev_rgb; @@ -1693,6 +1749,9 @@ skip_rgb: =20 cfg->bmap_1 =3D bmap_1; cfg->bmap_2 =3D bmap_2; + cfg->bmap_format =3D quirks && quirks->bmap_format ? + quirks->bmap_format : OXP_BMAP_FORMAT_DEFAULT; + cfg->bmap_page_3 =3D quirks && quirks->bmap_page_3; oxp_reset_buttons(cfg); INIT_DELAYED_WORK(&cfg->oxp_btn_queue, oxp_btn_queue_fn); =20 @@ -1720,6 +1779,7 @@ err_quiesce: static int oxp_hid_probe(struct hid_device *hdev, const struct hid_device_id *id) { + const struct quirk_entry *quirks; int ret; u16 up; =20 @@ -1738,12 +1798,16 @@ static int oxp_hid_probe(struct hid_device *hdev, } =20 up =3D get_usage_page(hdev); + quirks =3D oxp_get_quirks(); dev_dbg(&hdev->dev, "Got usage page %04x\n", up); =20 switch (up) { case GEN1_USAGE_PAGE: case GEN2_USAGE_PAGE: - ret =3D oxp_cfg_probe(hdev, up); + if (!oxp_is_cfg_interface(hdev, quirks)) + return 0; + + ret =3D oxp_cfg_probe(hdev, up, quirks); if (ret) { hid_hw_close(hdev); hid_hw_stop(hdev); From nobody Fri Sep 25 17:42:22 2026 Received: from mail-oo2-f43.google.com (mail-oo2-f43.google.com [74.125.231.171]) (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 3D3433AA50B for ; Thu, 10 Sep 2026 03:21:41 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.171 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789010504; cv=none; b=AaTUUApVt6pYOnBNiJ1V4nXKJpQRl9JjcsKSlLRAwi6PWrv/G2J4mWwneOasITKqUcthxfQMzFErZblihBJ78P/LsCjCQdwW2QPVrzXpO1fIqRWiUzfdDxtctDoQp2YtOchDqK4pWSemcpE49RUltyqBpV9MYaTw9j+1/u0yzmg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789010504; c=relaxed/simple; bh=n7PxzJwow6goQxqFXofo7b7F5m0GVCyGNZusi68VtWs=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=O2SuCKxNezHX16NKhMfu6FsIzwyo0qOcUV2hSCBX0hVcqGdtCpZfr/HXjjcZD7ItDClbEElqMo01G/mrP4ZYZGHc7Ob6eDJQa6xlCBrNB39zkbscKWKOPkuc69f75ItQWdaCWWu4zN0qrRvOeaHQcHLKuEttV6kMbyiezGNdAQM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=e05Cq2OQ; arc=none smtp.client-ip=74.125.231.171 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="e05Cq2OQ" Received: by mail-oo2-f43.google.com with SMTP id 46e09a7af769-7f4bdf4f0e8so645314a34.1 for ; Wed, 09 Sep 2026 20:21:41 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1789010500; x=1789615300; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=SYHmtxUSY9vp5nzuqjNrcemqcLc6QeYPUNm9qo4Y6tI=; b=e05Cq2OQs9OpA/6BOxW4QYp8cqeZkZ7goOkx3lgQehXQax8iQw9jqJ9/QAiPf93aYQ Rh2y5waHJY7ksVqa44YnabyN5VziOEPhGXWxKNLDrLWYxKxxoPWl4Ol6DJ+mV1RTmOrC n9rgn8LNmgB0L70yXJoFUYFkfCCbQV9s1sBgRnMYWT6icuVQXaGzuwC8M+gr9BTGqVIe ayH5PrFNGy/GzCHr3NwUWvVSUc8mI30EeyHFx91S1vtfxNlzMJ4YpO/1dMoAwO4jmCgG QaFTH33DeTaQohmwBVh8Bfx+hg58LrqdJFT/Lfm8Ew+/JGo6vMAKRn33AUKe3LWQCKMh EbrA== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1789010500; x=1789615300; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=SYHmtxUSY9vp5nzuqjNrcemqcLc6QeYPUNm9qo4Y6tI=; b=XGXjPDAHfYlTIpRpy6zfFeIhbVxpwrqS+F46yMitN9SgqUqq+nZDs+448Qn0753e4f bYMwtKx7Mqa5IaGacsfFQnciCaCPUAv9Iy736A8gp7bjSAEUUD4kMnrhi25vRnDgEZLX yfik5Vbj6YKvzBQ/D1KzB4P/aTwmcN/OjaXs4eizqiSyxMH5cWfasNRgoEhPozS7AQtJ 4dNXfAisrLTJFDzMi2/iUZQKpqryRFUKnPsIZc4JnF09B5pRsQX3DUnWgNIyXMYBq6fQ ggVXY0pTT8t74DSOzp5W2gNi9ypFEwoo2bqmeGJ0VZ9cpA+NRiYSiJ0G+25WCXgAnJBx 3xdw== X-Forwarded-Encrypted: i=1; AKwUvBx+d9IVkit021GZB7IlcB0A4c/VthUCmXPR8XEtdaY2pDtz2oR2gcjCYxUQC/VvUYbQHDu3toAq4E9C19E=@vger.kernel.org X-Gm-Message-State: AFuF++mnJ+ToWLT7dnyLzlu134clNgkujPPliICuTdE+mByJXtM8DZDk uIvxFwLdg1hgERtoj5S/rM++qOvLeaH/02C6SyJ3x4czOSV0qRhkeBog X-Gm-Gg: AYBFou3vcNrZmPKKM/ufmndT/+R8rjRAs4sAFu1Nccxig1BftUNAL7WSFLyk95Z7IGr aUrHW/DGSAwmunIUR2c5iLs2ZawK0rc5SjyWuUTf9zlQcTvD2hIWKExxYdf8PhPWayUXPv+3jKo 7n76YisEzR9x7otcL6HGeN/CR7nDXqcRtPrQl3EZBF5X83SHylPOskZFvQXxFe5hetroJBns0j+ lsukbQdXL35soF5TOc0ZT/nInX0uq1ctcZk/9ZCJiwEKrZGl8DwS1HCZK/cSTo+EaSBq+hj9R5T XbiSLBVxyiNtzG2ZKzM6Wv2qgIGuCZg5aMJqPlvTuIoN1pdhqVNCw11+voprU65ckbVLjfRdie5 MYAzUuAc4t5FQFNOtf5aYfvLdI5BFKvF7qqNAuoqlGX0E+MtVGQMXWUroa6D59DAsOiPQrTj0HR p3Df809NRuHtZq9OjcyL6Lv60ZQmk21tvgr0cOymL0URlmmF4zRvVE277N6GC5rM0WlIxoV0JDz SwDCV6tcYDowtkUlTNl0Ic1Ow7KBZpig5DR9HKc1dyUXhmpBcaxJQhXQw== X-Received: by 2002:a05:6830:3495:b0:801:b3e8:25ab with SMTP id 46e09a7af769-801b3e8a6c8mr4014082a34.2.1789010500192; Wed, 09 Sep 2026 20:21:40 -0700 (PDT) Received: from localhost ([76.187.192.44]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-7f9f6da9a49sm19758315a34.13.2026.09.09.20.21.39 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Wed, 09 Sep 2026 20:21:39 -0700 (PDT) From: Andrei Aldea To: Jiri Kosina , Benjamin Tissoires , "Derek J. Clark" Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Lee Jones , Pavel Machek , linux-leds@vger.kernel.org Subject: [PATCH 13/15] HID: hid-oxp: represent RGB LEDs with a common array Date: Wed, 9 Sep 2026 22:21:13 -0500 Message-ID: <20260910032115.28669-14-andrei1998@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260910032115.28669-1-andrei1998@gmail.com> References: <20260910032115.28669-1-andrei1998@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Move the existing joystick-ring class device, color components, delayed work, and cached settings into a per-LED wrapper owned by each HID configuration. Use a tagged state pointer so later LED types can share registration and work management without duplicating the lifecycle. Track only fully initialized work items and walk that count when quiescing, suspending, or resuming the configuration. Resolve LED callbacks through their containing wrapper, preserving the LED core's drvdata. Keep a single FULL joystick-ring LED and retain the existing Gen1/Gen2 55/57-byte RGB payloads, controls, and defaults. This commit adds no new hardware protocol or lighting zones. Assisted-by: LLM Reviewed-by: Derek J. Clark Signed-off-by: Andrei Aldea Tested-by: Derek J. Clark --- drivers/hid/hid-oxp.c | 433 +++++++++++++++++++++++++++++------------- 1 file changed, 297 insertions(+), 136 deletions(-) diff --git a/drivers/hid/hid-oxp.c b/drivers/hid/hid-oxp.c index 48fa916..55f8b47 100644 --- a/drivers/hid/hid-oxp.c +++ b/drivers/hid/hid-oxp.c @@ -198,6 +198,8 @@ struct oxp_bmap_page_2 { struct oxp_button_idx btn_m2; } __packed; =20 +struct oxp_rgb_led; + /* Hybrid devices expose RGB and controller configuration on separate HIDs= . */ struct oxp_hid_cfg { /* General HID state */ @@ -218,20 +220,13 @@ struct oxp_hid_cfg { u8 bmap_format; =20 /* RGB state */ - struct delayed_work oxp_rgb_queue; - struct mc_subled subled_info[3]; - struct led_classdev_mc *led_mc; - struct led_classdev_mc cdev; + struct oxp_rgb_led *rgb_leds; spinlock_t rgb_reply_lock; struct mutex rgb_mutex; /*serialize complete RGB transactions*/ - bool rgb_work_initialized; bool rgb_reply_pending; u8 rgb_reply_command; u8 rgb_reply_zone; - u8 rgb_brightness; - u8 rgb_effect; - u8 rgb_speed; - u8 rgb_en; + u8 rgb_led_count; }; =20 enum oxp_gamepad_mode_index { @@ -336,6 +331,31 @@ struct oxp_gen_2_rgb_report { u8 effect; } __packed; =20 +enum oxp_rgb_type { + OXP_RGB_FULL, +}; + +struct oxp_rgb_full_state { + u8 brightness; + u8 enabled; + u8 effect; + u8 speed; +}; + +struct oxp_rgb_led { + struct mc_subled subled_info[3]; + struct led_classdev_mc mc_cdev; + struct delayed_work work; + struct oxp_hid_cfg *cfg; + void *state; + u8 type; +}; + +struct oxp_rgb_led_desc { + const char *name; + u8 type; +}; + struct oxp_attr { u8 index; }; @@ -352,29 +372,55 @@ static u16 get_usage_page(struct hid_device *hdev) return hdev->collection[0].usage >> 16; } =20 +static struct oxp_rgb_led *oxp_rgb_led_by_type(struct oxp_hid_cfg *cfg, u8= type) +{ + int i; + + for (i =3D 0; i < cfg->rgb_led_count; i++) + if (cfg->rgb_leds[i].type =3D=3D type) + return &cfg->rgb_leds[i]; + + return NULL; +} + +static struct oxp_rgb_full_state *oxp_rgb_full_state(struct oxp_rgb_led *l= ed) +{ + if (!led) + return NULL; + + switch (led->type) { + case OXP_RGB_FULL: + return led->state; + } + + return NULL; +} + static int oxp_hid_raw_event_gen_1(struct hid_device *hdev, struct hid_report *report, u8 *data, int size) { struct oxp_hid_cfg *cfg =3D hid_get_drvdata(hdev); - struct led_classdev_mc *led_mc =3D cfg->led_mc; + struct oxp_rgb_led *led =3D oxp_rgb_led_by_type(cfg, OXP_RGB_FULL); + struct oxp_rgb_full_state *state =3D oxp_rgb_full_state(led); struct oxp_gen_1_rgb_report *rgb_rep; + struct led_classdev_mc *led_mc; =20 - if (size < sizeof(*rgb_rep) || !led_mc) + if (size < sizeof(*rgb_rep) || !state) return 0; =20 if (data[1] !=3D OXP_FID_GEN1_RGB_REPLY) return 0; =20 + led_mc =3D &led->mc_cdev; rgb_rep =3D (struct oxp_gen_1_rgb_report *)data; /* Ensure we save monocolor as the list value */ - cfg->rgb_effect =3D rgb_rep->effect =3D=3D OXP_EFFECT_MONO_TRUE ? - OXP_EFFECT_MONO_LIST : - rgb_rep->effect; - cfg->rgb_speed =3D rgb_rep->speed; - cfg->rgb_en =3D rgb_rep->enabled =3D=3D 0 ? OXP_FEAT_DISABLED : - OXP_FEAT_ENABLED; - cfg->rgb_brightness =3D rgb_rep->brightness; + state->effect =3D rgb_rep->effect =3D=3D OXP_EFFECT_MONO_TRUE ? + OXP_EFFECT_MONO_LIST : rgb_rep->effect; + state->speed =3D rgb_rep->speed; + state->enabled =3D rgb_rep->enabled =3D=3D 0 ? OXP_FEAT_DISABLED : + OXP_FEAT_ENABLED; + state->brightness =3D rgb_rep->brightness; led_mc->led_cdev.brightness =3D rgb_rep->brightness * led_mc->led_cdev.max_brightness / 4; /* If monocolor had less than 100% brightness on the previous boot, @@ -441,8 +487,10 @@ static int oxp_hid_raw_event_gen_2(struct hid_device *= hdev, int size) { struct oxp_hid_cfg *cfg =3D hid_get_drvdata(hdev); - struct led_classdev_mc *led_mc =3D cfg->led_mc; + struct oxp_rgb_led *led =3D oxp_rgb_led_by_type(cfg, OXP_RGB_FULL); + struct oxp_rgb_full_state *state =3D oxp_rgb_full_state(led); struct oxp_gen_2_rgb_report *rgb_rep; + struct led_classdev_mc *led_mc; bool solicited =3D false; =20 if (size < OXP_STATUS_HEADER_SIZE) @@ -479,22 +527,22 @@ static int oxp_hid_raw_event_gen_2(struct hid_device = *hdev, =20 if (data[3] !=3D OXP_GET_PROPERTY) return 0; - if (size < sizeof(*rgb_rep) || !led_mc) + if (size < sizeof(*rgb_rep) || !state) return 0; =20 + led_mc =3D &led->mc_cdev; rgb_rep =3D (struct oxp_gen_2_rgb_report *)data; if (rgb_rep->enabled > OXP_FEAT_ENABLED || rgb_rep->speed > 9 || rgb_rep->brightness > 4) return 0; =20 /* Ensure we save monocolor as the list value */ - cfg->rgb_effect =3D rgb_rep->effect =3D=3D OXP_EFFECT_MONO_TRUE ? - OXP_EFFECT_MONO_LIST : - rgb_rep->effect; - cfg->rgb_speed =3D rgb_rep->speed; - cfg->rgb_en =3D rgb_rep->enabled =3D=3D 0 ? OXP_FEAT_DISABLED : - OXP_FEAT_ENABLED; - cfg->rgb_brightness =3D rgb_rep->brightness; + state->effect =3D rgb_rep->effect =3D=3D OXP_EFFECT_MONO_TRUE ? + OXP_EFFECT_MONO_LIST : rgb_rep->effect; + state->speed =3D rgb_rep->speed; + state->enabled =3D rgb_rep->enabled =3D=3D 0 ? OXP_FEAT_DISABLED : + OXP_FEAT_ENABLED; + state->brightness =3D rgb_rep->brightness; led_mc->led_cdev.brightness =3D rgb_rep->brightness * led_mc->led_cdev.max_brightness / 4; /* If monocolor had less than 100% brightness on the previous boot, @@ -1158,9 +1206,11 @@ static const struct attribute_group oxp_cfg_attrs_gr= oup =3D { .attrs =3D oxp_cfg_attrs, }; =20 -static int oxp_rgb_status_store(struct oxp_hid_cfg *cfg, u8 enabled, - u8 speed, u8 brightness) +static int oxp_rgb_status_store(struct oxp_rgb_led *led, u8 enabled, u8 sp= eed, + u8 brightness) { + struct oxp_rgb_full_state *state =3D oxp_rgb_full_state(led); + struct oxp_hid_cfg *cfg =3D led->cfg; u16 up =3D get_usage_page(cfg->hdev); u8 *data; =20 @@ -1170,12 +1220,12 @@ static int oxp_rgb_status_store(struct oxp_hid_cfg = *cfg, u8 enabled, switch (up) { case GEN1_USAGE_PAGE: data =3D (u8[4]) { OXP_SET_PROPERTY, enabled, speed, brightness }; - if (cfg->rgb_effect =3D=3D OXP_EFFECT_MONO_LIST) + if (state->effect =3D=3D OXP_EFFECT_MONO_LIST) data[3] =3D 0x04; return oxp_gen_1_property_out(cfg, OXP_FID_GEN1_RGB_SET, data, 4); case GEN2_USAGE_PAGE: data =3D (u8[6]) { OXP_SET_PROPERTY, 0x00, 0x02, enabled, speed, brightn= ess }; - if (cfg->rgb_effect =3D=3D OXP_EFFECT_MONO_LIST) + if (state->effect =3D=3D OXP_EFFECT_MONO_LIST) data[5] =3D 0x04; return oxp_gen_2_property_out(cfg, OXP_FID_GEN2_STATUS_EVENT, data, 6); default: @@ -1183,8 +1233,9 @@ static int oxp_rgb_status_store(struct oxp_hid_cfg *c= fg, u8 enabled, } } =20 -static ssize_t oxp_rgb_status_show(struct oxp_hid_cfg *cfg) +static ssize_t oxp_rgb_status_show(struct oxp_rgb_led *led) { + struct oxp_hid_cfg *cfg =3D led->cfg; u16 up =3D get_usage_page(cfg->hdev); u8 *data; =20 @@ -1202,19 +1253,21 @@ static ssize_t oxp_rgb_status_show(struct oxp_hid_c= fg *cfg) } } =20 -static int oxp_rgb_color_set(struct oxp_hid_cfg *cfg) +static int oxp_rgb_color_set(struct oxp_rgb_led *led) { - u8 br =3D cfg->led_mc->led_cdev.brightness; + struct led_classdev_mc *led_mc =3D &led->mc_cdev; + struct oxp_hid_cfg *cfg =3D led->cfg; u16 up =3D get_usage_page(cfg->hdev); + u8 br =3D led_mc->led_cdev.brightness; u8 green, red, blue; size_t size; u8 *data; int i; =20 - led_mc_calc_color_components(cfg->led_mc, br); - red =3D cfg->led_mc->subled_info[0].brightness; - green =3D cfg->led_mc->subled_info[1].brightness; - blue =3D cfg->led_mc->subled_info[2].brightness; + led_mc_calc_color_components(led_mc, br); + red =3D led_mc->subled_info[0].brightness; + green =3D led_mc->subled_info[1].brightness; + blue =3D led_mc->subled_info[2].brightness; =20 switch (up) { case GEN1_USAGE_PAGE: @@ -1242,8 +1295,10 @@ static int oxp_rgb_color_set(struct oxp_hid_cfg *cfg) } } =20 -static int oxp_rgb_effect_set(struct oxp_hid_cfg *cfg, u8 effect) +static int oxp_rgb_effect_set(struct oxp_rgb_led *led, u8 effect) { + struct oxp_rgb_full_state *state =3D oxp_rgb_full_state(led); + struct oxp_hid_cfg *cfg =3D led->cfg; u16 up =3D get_usage_page(cfg->hdev); u8 *data; int ret; @@ -1282,7 +1337,7 @@ static int oxp_rgb_effect_set(struct oxp_hid_cfg *cfg= , u8 effect) } break; case OXP_EFFECT_MONO_LIST: - ret =3D oxp_rgb_color_set(cfg); + ret =3D oxp_rgb_color_set(led); break; default: return -EINVAL; @@ -1291,26 +1346,31 @@ static int oxp_rgb_effect_set(struct oxp_hid_cfg *c= fg, u8 effect) if (ret) return ret; =20 - cfg->rgb_effect =3D effect; + state->effect =3D effect; =20 return 0; } =20 -static struct oxp_hid_cfg *oxp_rgb_cfg_from_dev(struct device *dev) +static struct oxp_rgb_led *oxp_rgb_led_from_dev(struct device *dev) { struct led_classdev *led_cdev =3D dev_get_drvdata(dev); struct led_classdev_mc *mc_cdev =3D lcdev_to_mccdev(led_cdev); =20 - return container_of(mc_cdev, struct oxp_hid_cfg, cdev); + return container_of(mc_cdev, struct oxp_rgb_led, mc_cdev); } =20 static ssize_t enabled_store(struct device *dev, struct device_attribute *= attr, const char *buf, size_t count) { - struct oxp_hid_cfg *cfg =3D oxp_rgb_cfg_from_dev(dev); + struct oxp_rgb_led *led =3D oxp_rgb_led_from_dev(dev); + struct oxp_hid_cfg *cfg =3D led->cfg; + struct oxp_rgb_full_state *state =3D oxp_rgb_full_state(led); int ret; u8 val; =20 + if (!state) + return -ENODEV; + ret =3D sysfs_match_string(oxp_feature_en_text, buf); if (ret < 0) return ret; @@ -1318,29 +1378,32 @@ static ssize_t enabled_store(struct device *dev, st= ruct device_attribute *attr, =20 guard(mutex)(&cfg->rgb_mutex); =20 - ret =3D oxp_rgb_status_store(cfg, val, cfg->rgb_speed, - cfg->rgb_brightness); + ret =3D oxp_rgb_status_store(led, val, state->speed, state->brightness); if (ret) return ret; =20 - cfg->rgb_en =3D val; + state->enabled =3D val; return count; } =20 static ssize_t enabled_show(struct device *dev, struct device_attribute *a= ttr, char *buf) { - struct oxp_hid_cfg *cfg =3D oxp_rgb_cfg_from_dev(dev); + struct oxp_rgb_led *led =3D oxp_rgb_led_from_dev(dev); + struct oxp_rgb_full_state *state =3D oxp_rgb_full_state(led); int ret; =20 - ret =3D oxp_rgb_status_show(cfg); + if (!state) + return -ENODEV; + + ret =3D oxp_rgb_status_show(led); if (ret) return ret; =20 - if (cfg->rgb_en >=3D ARRAY_SIZE(oxp_feature_en_text)) + if (state->enabled >=3D ARRAY_SIZE(oxp_feature_en_text)) return -EINVAL; =20 - return sysfs_emit(buf, "%s\n", oxp_feature_en_text[cfg->rgb_en]); + return sysfs_emit(buf, "%s\n", oxp_feature_en_text[state->enabled]); } static DEVICE_ATTR_RW(enabled); =20 @@ -1363,11 +1426,16 @@ static DEVICE_ATTR_RO(enabled_index); static ssize_t effect_store(struct device *dev, struct device_attribute *a= ttr, const char *buf, size_t count) { - struct oxp_hid_cfg *cfg =3D oxp_rgb_cfg_from_dev(dev); + struct oxp_rgb_led *led =3D oxp_rgb_led_from_dev(dev); + struct oxp_hid_cfg *cfg =3D led->cfg; + struct oxp_rgb_full_state *state =3D oxp_rgb_full_state(led); u8 old_effect; int ret; u8 val; =20 + if (!state) + return -ENODEV; + ret =3D sysfs_match_string(oxp_rgb_effect_text, buf); if (ret < 0) return ret; @@ -1375,19 +1443,19 @@ static ssize_t effect_store(struct device *dev, str= uct device_attribute *attr, val =3D ret; =20 guard(mutex)(&cfg->rgb_mutex); - old_effect =3D cfg->rgb_effect; - cfg->rgb_effect =3D val; + old_effect =3D state->effect; + state->effect =3D val; =20 - ret =3D oxp_rgb_status_store(cfg, cfg->rgb_en, cfg->rgb_speed, - cfg->rgb_brightness); + ret =3D oxp_rgb_status_store(led, state->enabled, state->speed, + state->brightness); if (ret) { - cfg->rgb_effect =3D old_effect; + state->effect =3D old_effect; return ret; } =20 - ret =3D oxp_rgb_effect_set(cfg, val); + ret =3D oxp_rgb_effect_set(led, val); if (ret) { - cfg->rgb_effect =3D old_effect; + state->effect =3D old_effect; return ret; } =20 @@ -1397,17 +1465,21 @@ static ssize_t effect_store(struct device *dev, str= uct device_attribute *attr, static ssize_t effect_show(struct device *dev, struct device_attribute *at= tr, char *buf) { - struct oxp_hid_cfg *cfg =3D oxp_rgb_cfg_from_dev(dev); + struct oxp_rgb_led *led =3D oxp_rgb_led_from_dev(dev); + struct oxp_rgb_full_state *state =3D oxp_rgb_full_state(led); int ret; =20 - ret =3D oxp_rgb_status_show(cfg); + if (!state) + return -ENODEV; + + ret =3D oxp_rgb_status_show(led); if (ret) return ret; =20 - if (cfg->rgb_effect >=3D ARRAY_SIZE(oxp_rgb_effect_text)) + if (state->effect >=3D ARRAY_SIZE(oxp_rgb_effect_text)) return -EINVAL; =20 - return sysfs_emit(buf, "%s\n", oxp_rgb_effect_text[cfg->rgb_effect]); + return sysfs_emit(buf, "%s\n", oxp_rgb_effect_text[state->effect]); } =20 static DEVICE_ATTR_RW(effect); @@ -1431,10 +1503,15 @@ static DEVICE_ATTR_RO(effect_index); static ssize_t speed_store(struct device *dev, struct device_attribute *at= tr, const char *buf, size_t count) { - struct oxp_hid_cfg *cfg =3D oxp_rgb_cfg_from_dev(dev); + struct oxp_rgb_led *led =3D oxp_rgb_led_from_dev(dev); + struct oxp_hid_cfg *cfg =3D led->cfg; + struct oxp_rgb_full_state *state =3D oxp_rgb_full_state(led); int ret; u8 val; =20 + if (!state) + return -ENODEV; + ret =3D kstrtou8(buf, 10, &val); if (ret) return ret; @@ -1444,28 +1521,32 @@ static ssize_t speed_store(struct device *dev, stru= ct device_attribute *attr, =20 guard(mutex)(&cfg->rgb_mutex); =20 - ret =3D oxp_rgb_status_store(cfg, cfg->rgb_en, val, cfg->rgb_brightness); + ret =3D oxp_rgb_status_store(led, state->enabled, val, state->brightness); if (ret) return ret; =20 - cfg->rgb_speed =3D val; + state->speed =3D val; return count; } =20 static ssize_t speed_show(struct device *dev, struct device_attribute *att= r, char *buf) { - struct oxp_hid_cfg *cfg =3D oxp_rgb_cfg_from_dev(dev); + struct oxp_rgb_led *led =3D oxp_rgb_led_from_dev(dev); + struct oxp_rgb_full_state *state =3D oxp_rgb_full_state(led); int ret; =20 - ret =3D oxp_rgb_status_show(cfg); + if (!state) + return -ENODEV; + + ret =3D oxp_rgb_status_show(led); if (ret) return ret; =20 - if (cfg->rgb_speed > 9) + if (state->speed > 9) return -EINVAL; =20 - return sysfs_emit(buf, "%hhu\n", cfg->rgb_speed); + return sysfs_emit(buf, "%hhu\n", state->speed); } static DEVICE_ATTR_RW(speed); =20 @@ -1476,49 +1557,64 @@ static ssize_t speed_range_show(struct device *dev, } static DEVICE_ATTR_RO(speed_range); =20 -static void oxp_rgb_queue_fn(struct work_struct *work) +static void oxp_rgb_full_queue(struct oxp_rgb_led *led, + struct oxp_rgb_full_state *state) { - struct oxp_hid_cfg *cfg =3D container_of(to_delayed_work(work), - struct oxp_hid_cfg, oxp_rgb_queue); - unsigned int max_brightness =3D cfg->led_mc->led_cdev.max_brightness; - unsigned int brightness =3D cfg->led_mc->led_cdev.brightness; + unsigned int max_brightness =3D led->mc_cdev.led_cdev.max_brightness; + unsigned int brightness =3D led->mc_cdev.led_cdev.brightness; + struct oxp_hid_cfg *cfg =3D led->cfg; u8 val =3D 4 * brightness / max_brightness; int ret; =20 - if (READ_ONCE(cfg->suspended) || READ_ONCE(cfg->removing)) - return; - guard(mutex)(&cfg->rgb_mutex); =20 - if (cfg->rgb_brightness !=3D val) { - ret =3D oxp_rgb_status_store(cfg, cfg->rgb_en, cfg->rgb_speed, val); + if (state->brightness !=3D val) { + ret =3D oxp_rgb_status_store(led, state->enabled, state->speed, val); if (ret) - dev_err(cfg->led_mc->led_cdev.dev, + dev_err(led->mc_cdev.led_cdev.dev, "Error: Failed to write RGB Status: %i\n", ret); =20 - cfg->rgb_brightness =3D val; + state->brightness =3D val; } =20 - if (cfg->rgb_effect !=3D OXP_EFFECT_MONO_LIST) + if (state->effect !=3D OXP_EFFECT_MONO_LIST) return; =20 - ret =3D oxp_rgb_effect_set(cfg, cfg->rgb_effect); + ret =3D oxp_rgb_effect_set(led, state->effect); if (ret) - dev_err(cfg->led_mc->led_cdev.dev, "Error: Failed to write RGB color: %i= \n", - ret); + dev_err(led->mc_cdev.led_cdev.dev, + "Error: Failed to write RGB color: %i\n", ret); +} + +static void oxp_rgb_queue_fn(struct work_struct *work) +{ + struct oxp_rgb_led *led =3D container_of(to_delayed_work(work), + struct oxp_rgb_led, work); + struct oxp_hid_cfg *cfg =3D led->cfg; + + if (READ_ONCE(cfg->suspended) || READ_ONCE(cfg->removing)) + return; + + switch (led->type) { + case OXP_RGB_FULL: + oxp_rgb_full_queue(led, oxp_rgb_full_state(led)); + break; + } } =20 static void oxp_rgb_brightness_set(struct led_classdev *led_cdev, enum led_brightness brightness) { struct led_classdev_mc *mc_cdev =3D lcdev_to_mccdev(led_cdev); - struct oxp_hid_cfg *cfg =3D container_of(mc_cdev, struct oxp_hid_cfg, cde= v); + struct oxp_rgb_led *led =3D container_of(mc_cdev, struct oxp_rgb_led, + mc_cdev); + struct oxp_hid_cfg *cfg =3D led->cfg; =20 if (READ_ONCE(cfg->suspended) || READ_ONCE(cfg->removing)) return; =20 led_cdev->brightness =3D brightness; - mod_delayed_work(system_dfl_wq, &cfg->oxp_rgb_queue, msecs_to_jiffies(50)= ); + mod_delayed_work(system_dfl_wq, &led->work, msecs_to_jiffies(50)); } =20 static struct attribute *oxp_rgb_attrs[] =3D { @@ -1535,37 +1631,121 @@ static const struct attribute_group oxp_rgb_attr_g= roup =3D { .attrs =3D oxp_rgb_attrs, }; =20 -static const struct mc_subled oxp_rgb_subled_info[] =3D { +static const struct oxp_rgb_led_desc oxp_rgb_led_descs[] =3D { { + .name =3D "oxp:rgb:joystick_rings", + .type =3D OXP_RGB_FULL, + }, +}; + +static int oxp_rgb_led_init(struct oxp_hid_cfg *cfg, struct oxp_rgb_led *l= ed, + const struct oxp_rgb_led_desc *desc) +{ + struct oxp_rgb_full_state *full_state; + struct hid_device *hdev =3D cfg->hdev; + u8 green; + u8 blue; + u8 red; + + led->cfg =3D cfg; + led->type =3D desc->type; + + switch (led->type) { + case OXP_RGB_FULL: + full_state =3D devm_kzalloc(&hdev->dev, sizeof(*full_state), + GFP_KERNEL); + if (!full_state) + return -ENOMEM; + led->state =3D full_state; + led->mc_cdev.led_cdev.brightness =3D 0x64; + red =3D 0x24; + green =3D 0x22; + blue =3D 0x99; + break; + default: + return -EINVAL; + } + + led->subled_info[0] =3D (struct mc_subled) { .color_index =3D LED_COLOR_ID_RED, - .intensity =3D 0x24, + .intensity =3D red, .max_intensity =3D 0xff, .channel =3D 0x1, - }, - { + }; + led->subled_info[1] =3D (struct mc_subled) { .color_index =3D LED_COLOR_ID_GREEN, - .intensity =3D 0x22, + .intensity =3D green, .max_intensity =3D 0xff, .channel =3D 0x2, - }, - { + }; + led->subled_info[2] =3D (struct mc_subled) { .color_index =3D LED_COLOR_ID_BLUE, - .intensity =3D 0x99, + .intensity =3D blue, .max_intensity =3D 0xff, .channel =3D 0x3, - }, -}; + }; + led->mc_cdev.led_cdev.name =3D desc->name; + led->mc_cdev.led_cdev.color =3D LED_COLOR_ID_RGB; + led->mc_cdev.led_cdev.max_brightness =3D 0x64; + led->mc_cdev.led_cdev.brightness_set =3D oxp_rgb_brightness_set; + led->mc_cdev.num_colors =3D ARRAY_SIZE(led->subled_info); + led->mc_cdev.subled_info =3D led->subled_info; + INIT_DELAYED_WORK(&led->work, oxp_rgb_queue_fn); =20 -static const struct led_classdev_mc oxp_cdev_rgb =3D { - .led_cdev =3D { - .name =3D "oxp:rgb:joystick_rings", - .color =3D LED_COLOR_ID_RGB, - .brightness =3D 0x64, - .max_brightness =3D 0x64, - .brightness_set =3D oxp_rgb_brightness_set, - }, - .num_colors =3D ARRAY_SIZE(oxp_rgb_subled_info), -}; + return 0; +} + +static int oxp_rgb_leds_register(struct oxp_hid_cfg *cfg) +{ + int led_count =3D ARRAY_SIZE(oxp_rgb_led_descs); + struct hid_device *hdev =3D cfg->hdev; + struct oxp_rgb_led *led; + int ret; + int i; + + cfg->rgb_leds =3D devm_kcalloc(&hdev->dev, led_count, + sizeof(*cfg->rgb_leds), GFP_KERNEL); + if (!cfg->rgb_leds) + return -ENOMEM; + + for (i =3D 0; i < led_count; i++) { + led =3D &cfg->rgb_leds[i]; + ret =3D oxp_rgb_led_init(cfg, led, &oxp_rgb_led_descs[i]); + if (ret) + return ret; + cfg->rgb_led_count++; + + ret =3D devm_led_classdev_multicolor_register(&hdev->dev, + &led->mc_cdev); + if (ret) + return dev_err_probe(&hdev->dev, ret, + "Failed to create RGB device\n"); + + ret =3D devm_device_add_group(led->mc_cdev.led_cdev.dev, + &oxp_rgb_attr_group); + if (ret) + return dev_err_probe(led->mc_cdev.led_cdev.dev, ret, + "Failed to create RGB configuration attributes\n"); + } + + return 0; +} + +static void oxp_rgb_disable_works(struct oxp_hid_cfg *cfg) +{ + int i; + + for (i =3D 0; i < cfg->rgb_led_count; i++) + disable_delayed_work_sync(&cfg->rgb_leds[i].work); +} + +static void oxp_rgb_enable_works(struct oxp_hid_cfg *cfg) +{ + int i; + + for (i =3D 0; i < cfg->rgb_led_count; i++) + enable_delayed_work(&cfg->rgb_leds[i].work); +} =20 static struct quirk_entry quirk_hybrid_mcu =3D { .hybrid_mcu =3D true, @@ -1644,8 +1824,7 @@ static void oxp_quiesce_work(struct oxp_hid_cfg *cfg) scoped_guard(spinlock_irqsave, &cfg->rgb_reply_lock) { cfg->rgb_reply_pending =3D false; } - if (cfg->rgb_work_initialized) - disable_delayed_work_sync(&cfg->oxp_rgb_queue); + oxp_rgb_disable_works(cfg); if (cfg->gen2_work_initialized) { disable_delayed_work_sync(&cfg->oxp_btn_queue); disable_delayed_work_sync(&cfg->oxp_mcu_init); @@ -1680,6 +1859,7 @@ static int oxp_cfg_probe(struct hid_device *hdev, u16= up, { struct oxp_bmap_page_1 *bmap_1; struct oxp_bmap_page_2 *bmap_2; + struct oxp_rgb_led *rgb_led; struct oxp_hid_cfg *cfg; int ret; =20 @@ -1701,31 +1881,14 @@ static int oxp_cfg_probe(struct hid_device *hdev, u= 16 up, if (up =3D=3D GEN2_USAGE_PAGE && quirks && quirks->hybrid_mcu) goto skip_rgb; =20 - cfg->cdev =3D oxp_cdev_rgb; - memcpy(cfg->subled_info, oxp_rgb_subled_info, sizeof(cfg->subled_info)); - cfg->cdev.subled_info =3D cfg->subled_info; - cfg->led_mc =3D &cfg->cdev; - - INIT_DELAYED_WORK(&cfg->oxp_rgb_queue, oxp_rgb_queue_fn); - cfg->rgb_work_initialized =3D true; - ret =3D devm_led_classdev_multicolor_register(&hdev->dev, cfg->led_mc); - if (ret) { - dev_err_probe(&hdev->dev, ret, - "Failed to create RGB device\n"); - goto err_quiesce; - } - - ret =3D devm_device_add_group(cfg->led_mc->led_cdev.dev, - &oxp_rgb_attr_group); - if (ret) { - dev_err_probe(cfg->led_mc->led_cdev.dev, ret, - "Failed to create RGB configuration attributes\n"); + ret =3D oxp_rgb_leds_register(cfg); + if (ret) goto err_quiesce; - } =20 - ret =3D oxp_rgb_status_show(cfg); + rgb_led =3D oxp_rgb_led_by_type(cfg, OXP_RGB_FULL); + ret =3D oxp_rgb_status_show(rgb_led); if (ret) - dev_warn(cfg->led_mc->led_cdev.dev, + dev_warn(rgb_led->mc_cdev.led_cdev.dev, "Failed to query RGB initial state: %i\n", ret); =20 /* Below features are only implemented in gen 2 */ @@ -1841,8 +2004,7 @@ static int __maybe_unused oxp_hid_suspend(struct hid_= device *hdev, scoped_guard(spinlock_irqsave, &cfg->rgb_reply_lock) { cfg->rgb_reply_pending =3D false; } - if (cfg->rgb_work_initialized) - disable_delayed_work_sync(&cfg->oxp_rgb_queue); + oxp_rgb_disable_works(cfg); if (cfg->gen2_work_initialized) { disable_delayed_work_sync(&cfg->oxp_btn_queue); disable_delayed_work_sync(&cfg->oxp_mcu_init); @@ -1860,8 +2022,7 @@ static int __maybe_unused oxp_hid_resume(struct hid_d= evice *hdev) READ_ONCE(cfg->removing)) return 0; =20 - if (cfg->rgb_work_initialized) - enable_delayed_work(&cfg->oxp_rgb_queue); + oxp_rgb_enable_works(cfg); if (cfg->gen2_work_initialized) { enable_delayed_work(&cfg->oxp_btn_queue); enable_delayed_work(&cfg->oxp_mcu_init); From nobody Fri Sep 25 17:42:22 2026 Received: from mail-oo2-f42.google.com (mail-oo2-f42.google.com [74.125.231.170]) (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 560BC3AB283 for ; Thu, 10 Sep 2026 03:21:42 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789010506; cv=none; b=FA288SUfL2WEWDGZeVevhZZlgvUWGIOWBKMe4LkIHwSpQdT5eOg9B95R1MqaET1/D6k/rlRJyNnFH/O9P8bdkDXpu+O5dEVOALCuDzDxF0ihTkTl0r9q8dyPrWVVwMgugCivjmOFykAt7q5cGB4wsSKrXzc6s9KeRgq7xQ2KrsA= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789010506; c=relaxed/simple; bh=oOmme98cN77qhxcr1UclB0CTKGdtpK1qXReZJvuLZJ8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=miVgJh2AQgFNOEXdqm/cxhA0CMTFD+a0tqbdDBKQcfcdSA3cXKywvLaAtHYyTd5NYdYiF7x2pR41saNvcOmoaUVkT25uYGccHKEfmbCG+qhlTx4r6qFnAjTdMm06CnqoUpA96PjUG1C7MoP+kk39qv9H1Zgma5kaWATY9flROq0= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=fMy57ia+; arc=none smtp.client-ip=74.125.231.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="fMy57ia+" Received: by mail-oo2-f42.google.com with SMTP id 46e09a7af769-801d2f7a00eso164371a34.0 for ; Wed, 09 Sep 2026 20:21:42 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1789010501; x=1789615301; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=ZH4D7c9SuIpifcHsGJ8T7ktYODVw6NknltU48ClA/nM=; b=fMy57ia+eJlh1QX6oC91zxr1mnM2D7njKztNLgeflO8gtgh7uHh5j4oLjDrGJiQ3On 8z0/O9dASOWk51honbQrIcR6W/bRkCu2setumN+99wtbfiHttsP31n6FFHcubut5DXJZ cCeOzjzkMqJGnmJY8V1nQg30zKkVCXaMH6tJS8rWL/D7gIJQD6qA8Jep00k4gviMmKOn YwH858KhVhrrAROYgVIBxqDOFRAqT1nnbc3rjEk0yrvCRTefIfksOJLAppdbPq/8adm+ LD68SkCd16Y+CRMJ49YXsR6pK2xGW52DzBHUFslHHAGIjhgL6cXsmid718GM41P74D5B BpLw== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1789010501; x=1789615301; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=ZH4D7c9SuIpifcHsGJ8T7ktYODVw6NknltU48ClA/nM=; b=TxkyLCTS0a0HI2SoPMWM6HsLEMq6V1DfcUnhRocT2Yaa/BRWpjUA3TmOXHeXItYzXH +fpMf5dbBd/HmcFEdNU8fMPJ/oCNgYgv3FLHRhTAiiL5s67L60wh3sBNgNU2EBBoPCe7 WW4+eYjKQphy5HSqVEUA0AZ+veHXo6HvKK94Yex+ggmVX7xbu6oSkhMNfrEGgK6dJVPy iGz7PKsnJOcdqQdTZ4jgs+AR8S++qDsEJmFd9H56lsIWx7bDERkacuJdxp/WlvFHGulU L6WKZRBAqDKM3ZUD1xtIcTaXp/0oVboiLf63nzgQYb7EjnFhFe46xWm2/4roCc9semAj XX8g== X-Forwarded-Encrypted: i=1; AKwUvBys8+9CgcIpPqHMSYxB56SWCcLyV8NvQTOgYFynHRqIVSsbN3FtqRwG1vvXhJG0GBF0I9sbM3/C0toDMds=@vger.kernel.org X-Gm-Message-State: AFuF++nYxWkkVbhWsVR1Vjvk/MsQtanzExwHbrBnddQLTwlvMAFGViZk cQqXX5GgWIPz6NanlQB9bjLbibEbBKKF0iAp+u3VCRhXo9HY3ovyySep3mB4wE68Cty3VFzK X-Gm-Gg: AYBFou00vXRnkf9rvYtYLMMmaOP480UFxf1dkWyIAWxVWWUT7y2xnhEODcyJcok7her IY8mripUqsv+yFXZTK1nWyd2s7HsPyYJXjiT3RqiEw57KjLNGLDlxLPH+mFwNxAqtFTGGc5WfrM FvDTOioL7JaCafCQvIfb2Mz1ycKG3w0MdNqJnXvELIm/BrX59GVZ12Qd88oN2LngjBw+AbDNjup qFnq74JFsayy/OhJ/BNXAbr9aUak+KH7pYE1O/4d/CsvRAWeD6hqeCUq1cVRqS4/sbUT9v+bYi+ BqbBiGjrAVHvLK6OjfzCNgDTfzrQZ9SmXAdkfBGMV7/El7HNbl9hcn0AqJBFXI54b/3J4EKmU6H Xhh0t5u10rNiF4sLis6CIrIDtS1IEXE45qDeguIQmzUT1jetiI7I4f+XzDddnptN9w9c9dzyss6 XDZl4WDyTipjthfdcYHqrJoW7VHLpnFlP5Q7uQTeLcPa8FWXo0nPNF/LpC+0VqbOLfu5xRh/XM6 KOz3NLzajz5r+qA+bT+cp6IYv7zDkZ7W31Xi8Cbl/egGfn4i+z64HE= X-Received: by 2002:a05:6830:91a:b0:7f4:5b0c:36a4 with SMTP id 46e09a7af769-7fb7be96d99mr19851779a34.3.1789010501089; Wed, 09 Sep 2026 20:21:41 -0700 (PDT) Received: from localhost ([76.187.192.44]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-7f9f6da9a49sm19758315a34.13.2026.09.09.20.21.40 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Wed, 09 Sep 2026 20:21:40 -0700 (PDT) From: Andrei Aldea To: Jiri Kosina , Benjamin Tissoires , "Derek J. Clark" Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Lee Jones , Pavel Machek , linux-leds@vger.kernel.org Subject: [PATCH 14/15] HID: hid-oxp: add Gen3 joystick ring RGB support Date: Wed, 9 Sep 2026 22:21:14 -0500 Message-ID: <20260910032115.28669-15-andrei1998@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260910032115.28669-1-andrei1998@gmail.com> References: <20260910032115.28669-1-andrei1998@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Select the X2/Gen3 RGB protocol early for the ONEXPLAYER 3 and X2 Mini Pro. These controllers share the Gen2 HID usage page but require a 59-byte color payload and writes to ring zones 1, 2 and 7. Keep the Gen1 55-byte and legacy Gen2 57-byte color builders separate and unchanged. Add dedicated Gen3 status, color, effect and brightness paths. Check its known command/zone acknowledgments, retain cached color and effect when status cannot report them, and retry one complete ring pass on failure. Legacy transport success does not depend on receiving an acknowledgment. Restore cached X2 RGB once after the final controller-mode change. Generic ACK discrimination and suspend work management are preceding fixes; this patch does not add RGB replay to older controllers. Assisted-by: LLM Reviewed-by: Derek J. Clark Signed-off-by: Andrei Aldea --- drivers/hid/hid-oxp.c | 464 ++++++++++++++++++++++++++++++++++++------ 1 file changed, 398 insertions(+), 66 deletions(-) diff --git a/drivers/hid/hid-oxp.c b/drivers/hid/hid-oxp.c index 55f8b47..9a46602 100644 --- a/drivers/hid/hid-oxp.c +++ b/drivers/hid/hid-oxp.c @@ -25,8 +25,8 @@ #include "hid-ids.h" =20 #define OXP_PACKET_SIZE 64 -#define OXP_STATUS_HEADER_SIZE 6 -#define OXP_STATUS_ACK 0x20 +#define OXP_GEN2_EVENT_HEADER_SIZE 6 +#define OXP_GEN2_RGB_ACK 0x20 =20 #define GEN1_MESSAGE_ID 0xff #define GEN2_MESSAGE_ID 0x3f @@ -214,19 +214,20 @@ struct oxp_hid_cfg { struct oxp_bmap_page_1 *bmap_1; struct oxp_bmap_page_2 *bmap_2; bool gen2_work_initialized; - bool bmap_page_3; u8 rumble_intensity; + bool bmap_page_3; u8 gamepad_mode; u8 bmap_format; =20 /* RGB state */ struct oxp_rgb_led *rgb_leds; - spinlock_t rgb_reply_lock; + spinlock_t rgb_reply_lock; /* Protect pending RGB acknowledgment state. */ struct mutex rgb_mutex; /*serialize complete RGB transactions*/ bool rgb_reply_pending; u8 rgb_reply_command; u8 rgb_reply_zone; u8 rgb_led_count; + bool x2_rgb; }; =20 enum oxp_gamepad_mode_index { @@ -315,6 +316,15 @@ struct oxp_gen_1_rgb_report { u8 blue; } __packed; =20 +struct oxp_gen_2_event_header { + u8 report_id; + u8 message_id; + u8 padding; + u8 command; + u8 zone; + u8 status; +} __packed; + struct oxp_gen_2_rgb_report { u8 report_id; u8 header_id; @@ -331,6 +341,21 @@ struct oxp_gen_2_rgb_report { u8 effect; } __packed; =20 +struct oxp_rgb_color { + u8 red; + u8 green; + u8 blue; +} __packed; + +struct oxp_gen_3_rgb_color_report { + u8 effect; + u8 zone; + u8 mode; + struct oxp_rgb_color colors[18]; + u8 final_red; + u8 final_green; +} __packed; + enum oxp_rgb_type { OXP_RGB_FULL, }; @@ -361,10 +386,11 @@ struct oxp_attr { }; =20 struct quirk_entry { - bool hybrid_mcu; - bool bmap_page_3; u8 cfg_interface_num; + bool bmap_page_3; + bool hybrid_mcu; u8 bmap_format; + bool x2_rgb; }; =20 static u16 get_usage_page(struct hid_device *hdev) @@ -443,6 +469,51 @@ static int oxp_gen_2_property_out(struct oxp_hid_cfg *= cfg, static int oxp_set_buttons(struct oxp_hid_cfg *cfg); static int oxp_rumble_intensity_set(struct oxp_hid_cfg *cfg, u8 intensity); =20 +static int oxp_rgb_status_store(struct oxp_rgb_led *led, u8 enabled, u8 sp= eed, + u8 brightness); +static int oxp_rgb_effect_set(struct oxp_rgb_led *led, u8 effect); + +static void oxp_rgb_restore(struct oxp_hid_cfg *cfg) +{ + struct oxp_rgb_full_state *state; + struct oxp_rgb_led *led; + int ret; + int i; + + if (!cfg->x2_rgb) + return; + + guard(mutex)(&cfg->rgb_mutex); + for (i =3D 0; i < cfg->rgb_led_count; i++) { + led =3D &cfg->rgb_leds[i]; + switch (led->type) { + case OXP_RGB_FULL: + state =3D led->state; + if (state->effect =3D=3D OXP_UNKNOWN) + break; + + ret =3D oxp_rgb_status_store(led, state->enabled, + state->speed, + state->brightness); + if (ret) { + dev_err(&cfg->hdev->dev, + "Error: Failed to restore RGB status: %i\n", + ret); + break; + } + if (state->enabled =3D=3D OXP_FEAT_DISABLED) + break; + + ret =3D oxp_rgb_effect_set(led, state->effect); + if (ret) + dev_err(&cfg->hdev->dev, + "Error: Failed to restore RGB effect: %i\n", + ret); + break; + } + } +} + static void oxp_mcu_init_fn(struct work_struct *work) { struct oxp_hid_cfg *cfg =3D container_of(to_delayed_work(work), @@ -459,92 +530,100 @@ static void oxp_mcu_init_fn(struct work_struct *work) dev_err(&cfg->hdev->dev, "Error: Failed to set button mapping: %i\n", ret); =20 - /* Cycle the gamepad mode */ - ret =3D oxp_gen_2_property_out(cfg, OXP_FID_GEN2_TOGGLE_MODE, gp_mode_dat= a, 3); + /* Cycle the gamepad mode before restoring lighting. */ + ret =3D oxp_gen_2_property_out(cfg, OXP_FID_GEN2_TOGGLE_MODE, + gp_mode_data, sizeof(gp_mode_data)); if (ret) dev_err(&cfg->hdev->dev, "Error: Failed to set gamepad mode: %i\n", ret); =20 - /* Remainder only applies for xinput mode */ + if (cfg->gamepad_mode !=3D OXP_GP_MODE_DEBUG) { + gp_mode_data[0] =3D OXP_GP_MODE_XINPUT; + ret =3D oxp_gen_2_property_out(cfg, OXP_FID_GEN2_TOGGLE_MODE, + gp_mode_data, sizeof(gp_mode_data)); + if (ret) + dev_err(&cfg->hdev->dev, + "Error: Failed to set gamepad mode: %i\n", ret); + } + + /* The final mode change must precede the single RGB restore. */ + oxp_rgb_restore(cfg); if (cfg->gamepad_mode =3D=3D OXP_GP_MODE_DEBUG) return; =20 - gp_mode_data[0] =3D OXP_GP_MODE_XINPUT; - ret =3D oxp_gen_2_property_out(cfg, OXP_FID_GEN2_TOGGLE_MODE, gp_mode_dat= a, 3); - if (ret) - dev_err(&cfg->hdev->dev, - "Error: Failed to set gamepad mode: %i\n", ret); - - /* Set vibration level */ ret =3D oxp_rumble_intensity_set(cfg, cfg->rumble_intensity); if (ret) dev_err(&cfg->hdev->dev, "Error: Failed to set rumble intensity: %i\n", ret); } =20 -static int oxp_hid_raw_event_gen_2(struct hid_device *hdev, - struct hid_report *report, u8 *data, - int size) +static bool oxp_gen_2_rgb_event(struct oxp_hid_cfg *cfg, + const struct oxp_gen_2_event_header *header) { - struct oxp_hid_cfg *cfg =3D hid_get_drvdata(hdev); - struct oxp_rgb_led *led =3D oxp_rgb_led_by_type(cfg, OXP_RGB_FULL); - struct oxp_rgb_full_state *state =3D oxp_rgb_full_state(led); - struct oxp_gen_2_rgb_report *rgb_rep; - struct led_classdev_mc *led_mc; bool solicited =3D false; =20 - if (size < OXP_STATUS_HEADER_SIZE) - return 0; - - if (data[0] !=3D OXP_FID_GEN2_STATUS_EVENT) - return 0; - - /* A monocolor acknowledgment is not an MCU reset notification. */ - if (data[5] =3D=3D OXP_STATUS_ACK) { + if (header->status =3D=3D OXP_GEN2_RGB_ACK) { scoped_guard(spinlock_irqsave, &cfg->rgb_reply_lock) { if (cfg->rgb_reply_pending && - data[3] =3D=3D cfg->rgb_reply_command && - data[4] =3D=3D cfg->rgb_reply_zone) { + header->command =3D=3D cfg->rgb_reply_command && + header->zone =3D=3D cfg->rgb_reply_zone) { cfg->rgb_reply_pending =3D false; solicited =3D true; } } if (solicited) - return 0; + return true; } =20 - /* Sent ~6s after resume event, indicating the MCU has fully reset. - * Re-apply our settings after this has been received. - */ - if (data[3] =3D=3D OXP_EFFECT_MONO_TRUE) { - if (READ_ONCE(cfg->gen2_work_initialized) && - !READ_ONCE(cfg->suspended) && - !READ_ONCE(cfg->removing)) - mod_delayed_work(system_dfl_wq, &cfg->oxp_mcu_init, - msecs_to_jiffies(50)); - return 0; - } + /* A color-write acknowledgment must not be treated as an MCU reset. */ + if (header->command !=3D OXP_EFFECT_MONO_TRUE) + return false; + + /* An unsolicited event is sent after the MCU resets on resume. */ + if (READ_ONCE(cfg->gen2_work_initialized) && + !READ_ONCE(cfg->suspended) && !READ_ONCE(cfg->removing)) + mod_delayed_work(system_dfl_wq, &cfg->oxp_mcu_init, + msecs_to_jiffies(50)); + + return true; +} + +static int oxp_hid_raw_event_gen_2(struct hid_device *hdev, + struct hid_report *report, u8 *data, + int size) +{ + struct oxp_hid_cfg *cfg =3D hid_get_drvdata(hdev); + struct oxp_rgb_led *led =3D oxp_rgb_led_by_type(cfg, OXP_RGB_FULL); + struct oxp_rgb_full_state *state =3D oxp_rgb_full_state(led); + struct oxp_gen_2_event_header *header =3D (void *)data; + struct oxp_gen_2_rgb_report *rgb_rep; =20 - if (data[3] !=3D OXP_GET_PROPERTY) + if (size < OXP_GEN2_EVENT_HEADER_SIZE) + return 0; + if (header->report_id !=3D OXP_FID_GEN2_STATUS_EVENT) + return 0; + if (oxp_gen_2_rgb_event(cfg, header)) + return 0; + if (header->command !=3D OXP_GET_PROPERTY) return 0; if (size < sizeof(*rgb_rep) || !state) return 0; =20 - led_mc =3D &led->mc_cdev; rgb_rep =3D (struct oxp_gen_2_rgb_report *)data; if (rgb_rep->enabled > OXP_FEAT_ENABLED || rgb_rep->speed > 9 || rgb_rep->brightness > 4) return 0; =20 - /* Ensure we save monocolor as the list value */ + /* Ensure we save monocolor as the list value. */ state->effect =3D rgb_rep->effect =3D=3D OXP_EFFECT_MONO_TRUE ? OXP_EFFECT_MONO_LIST : rgb_rep->effect; state->speed =3D rgb_rep->speed; state->enabled =3D rgb_rep->enabled =3D=3D 0 ? OXP_FEAT_DISABLED : - OXP_FEAT_ENABLED; + OXP_FEAT_ENABLED; state->brightness =3D rgb_rep->brightness; - led_mc->led_cdev.brightness =3D rgb_rep->brightness * - led_mc->led_cdev.max_brightness / 4; + led->mc_cdev.led_cdev.brightness =3D rgb_rep->brightness * + led->mc_cdev.led_cdev.max_brightness; + led->mc_cdev.led_cdev.brightness /=3D 4; /* If monocolor had less than 100% brightness on the previous boot, * there will be no reliable way to determine the real intensity. * Since intensity scaling is used with a hardware brightness set at max, @@ -552,9 +631,52 @@ static int oxp_hid_raw_event_gen_2(struct hid_device *= hdev, * prevent successive boots from lowering the brightness further. * Brightness will be "wrong" but the effect will remain the same visuall= y. */ - led_mc->subled_info[0].intensity =3D rgb_rep->red; - led_mc->subled_info[1].intensity =3D rgb_rep->green; - led_mc->subled_info[2].intensity =3D rgb_rep->blue; + led->mc_cdev.subled_info[0].intensity =3D rgb_rep->red; + led->mc_cdev.subled_info[1].intensity =3D rgb_rep->green; + led->mc_cdev.subled_info[2].intensity =3D rgb_rep->blue; + + return 0; +} + +static int oxp_hid_raw_event_gen_3(struct oxp_hid_cfg *cfg, + struct hid_report *report, u8 *data, + int size) +{ + struct oxp_rgb_led *led =3D oxp_rgb_led_by_type(cfg, OXP_RGB_FULL); + struct oxp_rgb_full_state *state =3D oxp_rgb_full_state(led); + struct oxp_gen_2_event_header *header =3D (void *)data; + struct oxp_gen_2_rgb_report *rgb_rep; + + if (size < OXP_GEN2_EVENT_HEADER_SIZE) + return 0; + if (header->report_id !=3D OXP_FID_GEN2_STATUS_EVENT) + return 0; + if (oxp_gen_2_rgb_event(cfg, header)) + return 0; + if (header->command !=3D OXP_GET_PROPERTY) + return 0; + if (size < sizeof(*rgb_rep) || !state) + return 0; + + rgb_rep =3D (struct oxp_gen_2_rgb_report *)data; + if (rgb_rep->enabled > OXP_FEAT_ENABLED || rgb_rep->speed > 9 || + rgb_rep->brightness > 4) + return 0; + + /* Gen3 status replies do not report the current effect or RGB color. */ + state->speed =3D rgb_rep->speed; + state->enabled =3D rgb_rep->enabled =3D=3D 0 ? OXP_FEAT_DISABLED : + OXP_FEAT_ENABLED; + /* + * Monocolor uses scaled channels at maximum hardware brightness. + * Retain the requested brightness rather than replacing it with 100%. + */ + if (state->effect !=3D OXP_EFFECT_MONO_LIST) { + state->brightness =3D rgb_rep->brightness; + led->mc_cdev.led_cdev.brightness =3D rgb_rep->brightness * + led->mc_cdev.led_cdev.max_brightness; + led->mc_cdev.led_cdev.brightness /=3D 4; + } =20 return 0; } @@ -574,6 +696,9 @@ static int oxp_hid_raw_event(struct hid_device *hdev, s= truct hid_report *report, case GEN1_USAGE_PAGE: return oxp_hid_raw_event_gen_1(hdev, report, data, size); case GEN2_USAGE_PAGE: + /* X2/Gen3 controllers share the Gen2 HID usage page. */ + if (cfg->x2_rgb) + return oxp_hid_raw_event_gen_3(cfg, report, data, size); return oxp_hid_raw_event_gen_2(hdev, report, data, size); default: break; @@ -584,9 +709,11 @@ static int oxp_hid_raw_event(struct hid_device *hdev, = struct hid_report *report, =20 static int mcu_property_out(struct oxp_hid_cfg *cfg, u8 *header, size_t header_size, u8 *data, size_t data_size, - u8 *footer, size_t footer_size) + u8 *footer, size_t footer_size, bool expect_rgb_ack) { unsigned char *dmabuf __free(kfree) =3D kzalloc(OXP_PACKET_SIZE, GFP_KERN= EL); + u8 rgb_command =3D 0; + u8 rgb_zone =3D 0; bool rgb_write; int ret; =20 @@ -602,13 +729,21 @@ static int mcu_property_out(struct oxp_hid_cfg *cfg, = u8 *header, if (READ_ONCE(cfg->suspended)) return -EHOSTDOWN; =20 + /* + * Track legacy monocolor replies to distinguish them from MCU resets. + * Only Gen3's known acknowledgment protocol makes a missing reply an + * error; retain transport-only success semantics for older controllers. + */ rgb_write =3D header_size && data_size > 1 && header[0] =3D=3D OXP_FID_GEN2_STATUS_EVENT && - data[0] =3D=3D OXP_EFFECT_MONO_TRUE; + data[0] !=3D OXP_GET_PROPERTY && + (expect_rgb_ack || data[0] =3D=3D OXP_EFFECT_MONO_TRUE); if (rgb_write) { + rgb_command =3D data[0]; + rgb_zone =3D data[1]; scoped_guard(spinlock_irqsave, &cfg->rgb_reply_lock) { - cfg->rgb_reply_command =3D data[0]; - cfg->rgb_reply_zone =3D data[1]; + cfg->rgb_reply_command =3D rgb_command; + cfg->rgb_reply_zone =3D rgb_zone; cfg->rgb_reply_pending =3D true; } } @@ -628,7 +763,13 @@ static int mcu_property_out(struct oxp_hid_cfg *cfg, u= 8 *header, =20 if (rgb_write) { scoped_guard(spinlock_irqsave, &cfg->rgb_reply_lock) { - cfg->rgb_reply_pending =3D false; + if (cfg->rgb_reply_pending && + cfg->rgb_reply_command =3D=3D rgb_command && + cfg->rgb_reply_zone =3D=3D rgb_zone) { + if (!ret && expect_rgb_ack) + ret =3D -ETIMEDOUT; + cfg->rgb_reply_pending =3D false; + } } } =20 @@ -642,7 +783,8 @@ static int oxp_gen_1_property_out(struct oxp_hid_cfg *c= fg, u8 header[] =3D { fid, GEN1_MESSAGE_ID }; size_t header_size =3D ARRAY_SIZE(header); =20 - return mcu_property_out(cfg, header, header_size, data, data_size, NULL, = 0); + return mcu_property_out(cfg, header, header_size, data, data_size, + NULL, 0, false); } =20 static int oxp_gen_2_property_out(struct oxp_hid_cfg *cfg, @@ -655,7 +797,65 @@ static int oxp_gen_2_property_out(struct oxp_hid_cfg *= cfg, size_t footer_size =3D ARRAY_SIZE(footer); =20 return mcu_property_out(cfg, header, header_size, data, data_size, footer, - footer_size); + footer_size, false); +} + +static const u8 oxp_x2_rgb_zones[] =3D { 0x01, 0x02, 0x07 }; + +static int oxp_gen_3_property_out(struct oxp_hid_cfg *cfg, u8 *data, + u8 data_size) +{ + u8 header[] =3D { OXP_FID_GEN2_STATUS_EVENT, GEN2_MESSAGE_ID, 0x01 }; + u8 footer[] =3D { GEN2_MESSAGE_ID, OXP_FID_GEN2_STATUS_EVENT }; + size_t header_size =3D ARRAY_SIZE(header); + size_t footer_size =3D ARRAY_SIZE(footer); + + return mcu_property_out(cfg, header, header_size, data, data_size, + footer, footer_size, true); +} + +static int oxp_gen_3_rgb_property_out(struct oxp_hid_cfg *cfg, u8 *data, + u8 data_size) +{ + int first_err; + int attempt; + int ret; + int i; + + /* + * Each zone is addressed independently. Complete the pass even if one + * write fails so the remaining ring zones still receive the setting. + * Retry the full pass once to keep their requested settings identical. + */ + for (attempt =3D 0; attempt < 2; attempt++) { + first_err =3D 0; + for (i =3D 0; i < ARRAY_SIZE(oxp_x2_rgb_zones); i++) { + data[1] =3D oxp_x2_rgb_zones[i]; + ret =3D oxp_gen_3_property_out(cfg, data, data_size); + if (ret && !first_err) + first_err =3D ret; + } + if (!first_err) + return 0; + } + + return first_err; +} + +static void oxp_gen_3_rgb_fill_color(struct oxp_gen_3_rgb_color_report *re= port, + u8 command, u8 zone, u8 red, u8 green, + u8 blue) +{ + int i; + + memset(report, 0, sizeof(*report)); + report->effect =3D command; + report->zone =3D zone; + report->mode =3D 0x02; + for (i =3D 0; i < ARRAY_SIZE(report->colors); i++) + report->colors[i] =3D (struct oxp_rgb_color) { red, green, blue }; + report->final_red =3D red; + report->final_green =3D green; } =20 static ssize_t gamepad_mode_store(struct device *dev, @@ -1206,6 +1406,20 @@ static const struct attribute_group oxp_cfg_attrs_gr= oup =3D { .attrs =3D oxp_cfg_attrs, }; =20 +static int oxp_gen_3_rgb_status_store(struct oxp_rgb_led *led, u8 enabled, + u8 speed, u8 brightness) +{ + struct oxp_rgb_full_state *state =3D oxp_rgb_full_state(led); + u8 data[6] =3D { OXP_SET_PROPERTY, 0x00, 0x02, enabled, speed, brightness= }; + + if (!state) + return -ENODEV; + if (state->effect =3D=3D OXP_EFFECT_MONO_LIST) + data[5] =3D 0x04; + + return oxp_gen_3_rgb_property_out(led->cfg, data, sizeof(data)); +} + static int oxp_rgb_status_store(struct oxp_rgb_led *led, u8 enabled, u8 sp= eed, u8 brightness) { @@ -1214,6 +1428,11 @@ static int oxp_rgb_status_store(struct oxp_rgb_led *= led, u8 enabled, u8 speed, u16 up =3D get_usage_page(cfg->hdev); u8 *data; =20 + if (!state) + return -ENODEV; + if (cfg->x2_rgb) + return oxp_gen_3_rgb_status_store(led, enabled, speed, brightness); + /* Always default to max brightness and use intensity scaling when in * monocolor mode. */ @@ -1233,13 +1452,25 @@ static int oxp_rgb_status_store(struct oxp_rgb_led = *led, u8 enabled, u8 speed, } } =20 +static int oxp_gen_3_rgb_status_show(struct oxp_hid_cfg *cfg) +{ + u8 data[3] =3D { OXP_GET_PROPERTY, 0x07, 0x02 }; + + return oxp_gen_3_property_out(cfg, data, sizeof(data)); +} + static ssize_t oxp_rgb_status_show(struct oxp_rgb_led *led) { struct oxp_hid_cfg *cfg =3D led->cfg; u16 up =3D get_usage_page(cfg->hdev); u8 *data; =20 + if (!oxp_rgb_full_state(led)) + return -ENODEV; + guard(mutex)(&cfg->rgb_mutex); + if (cfg->x2_rgb) + return oxp_gen_3_rgb_status_show(cfg); =20 switch (up) { case GEN1_USAGE_PAGE: @@ -1253,6 +1484,22 @@ static ssize_t oxp_rgb_status_show(struct oxp_rgb_le= d *led) } } =20 +static int oxp_gen_3_rgb_color_set(struct oxp_rgb_led *led) +{ + struct led_classdev_mc *mc_cdev =3D &led->mc_cdev; + struct oxp_gen_3_rgb_color_report color_report; + u8 brightness =3D mc_cdev->led_cdev.brightness; + + led_mc_calc_color_components(mc_cdev, brightness); + oxp_gen_3_rgb_fill_color(&color_report, OXP_EFFECT_MONO_TRUE, 0x00, + mc_cdev->subled_info[0].brightness, + mc_cdev->subled_info[1].brightness, + mc_cdev->subled_info[2].brightness); + + return oxp_gen_3_rgb_property_out(led->cfg, (u8 *)&color_report, + sizeof(color_report)); +} + static int oxp_rgb_color_set(struct oxp_rgb_led *led) { struct led_classdev_mc *led_mc =3D &led->mc_cdev; @@ -1264,6 +1511,9 @@ static int oxp_rgb_color_set(struct oxp_rgb_led *led) u8 *data; int i; =20 + if (cfg->x2_rgb) + return oxp_gen_3_rgb_color_set(led); + led_mc_calc_color_components(led_mc, br); red =3D led_mc->subled_info[0].brightness; green =3D led_mc->subled_info[1].brightness; @@ -1281,6 +1531,7 @@ static int oxp_rgb_color_set(struct oxp_rgb_led *led) } return oxp_gen_1_property_out(cfg, OXP_FID_GEN1_RGB_SET, data, size); case GEN2_USAGE_PAGE: + /* Preserve the legacy payload and its two zero bytes before the footer.= */ size =3D 57; data =3D (u8[57]) { OXP_EFFECT_MONO_TRUE, 0x00, 0x02 }; =20 @@ -1295,6 +1546,29 @@ static int oxp_rgb_color_set(struct oxp_rgb_led *led) } } =20 +static int oxp_gen_3_rgb_effect_set(struct oxp_rgb_led *led, u8 effect) +{ + struct oxp_rgb_full_state *state =3D oxp_rgb_full_state(led); + u8 data[3] =3D { effect, 0x00, 0x02 }; + int ret; + + if (!state) + return -ENODEV; + + if (effect > OXP_UNKNOWN && effect < OXP_EFFECT_MONO_LIST) + ret =3D oxp_gen_3_rgb_property_out(led->cfg, data, sizeof(data)); + else if (effect =3D=3D OXP_EFFECT_MONO_LIST) + ret =3D oxp_gen_3_rgb_color_set(led); + else + return -EINVAL; + + if (ret) + return ret; + + state->effect =3D effect; + return 0; +} + static int oxp_rgb_effect_set(struct oxp_rgb_led *led, u8 effect) { struct oxp_rgb_full_state *state =3D oxp_rgb_full_state(led); @@ -1303,6 +1577,11 @@ static int oxp_rgb_effect_set(struct oxp_rgb_led *le= d, u8 effect) u8 *data; int ret; =20 + if (!state) + return -ENODEV; + if (cfg->x2_rgb) + return oxp_gen_3_rgb_effect_set(led, effect); + switch (effect) { case OXP_EFFECT_AURORA: case OXP_EFFECT_BIRTHDAY: @@ -1586,6 +1865,54 @@ static void oxp_rgb_full_queue(struct oxp_rgb_led *l= ed, "Error: Failed to write RGB color: %i\n", ret); } =20 +static void oxp_gen_3_rgb_queue(struct oxp_rgb_led *led, + struct oxp_rgb_full_state *state) +{ + unsigned int max_brightness =3D led->mc_cdev.led_cdev.max_brightness; + u8 old_effect; + unsigned int brightness =3D led->mc_cdev.led_cdev.brightness; + u8 enabled =3D brightness ? OXP_FEAT_ENABLED : OXP_FEAT_DISABLED; + struct oxp_hid_cfg *cfg =3D led->cfg; + u8 val =3D 4 * brightness / max_brightness; + int ret; + + guard(mutex)(&cfg->rgb_mutex); + old_effect =3D state->effect; + + if (!brightness) { + ret =3D oxp_gen_3_rgb_status_store(led, OXP_FEAT_DISABLED, 5, 0); + if (ret) { + dev_err(led->mc_cdev.led_cdev.dev, + "Error: Failed to disable RGB: %i\n", ret); + return; + } + + state->enabled =3D OXP_FEAT_DISABLED; + state->brightness =3D 0; + state->speed =3D 5; + return; + } + + /* Gen3 standard LED writes select solid color and follow brightness/off.= */ + state->effect =3D OXP_EFFECT_MONO_LIST; + ret =3D oxp_gen_3_rgb_status_store(led, enabled, 5, val); + if (ret) { + state->effect =3D old_effect; + dev_err(led->mc_cdev.led_cdev.dev, + "Error: Failed to write RGB status: %i\n", ret); + return; + } + + state->enabled =3D enabled; + state->brightness =3D val; + state->speed =3D 5; + + ret =3D oxp_gen_3_rgb_effect_set(led, OXP_EFFECT_MONO_LIST); + if (ret) + dev_err(led->mc_cdev.led_cdev.dev, + "Error: Failed to write RGB color: %i\n", ret); +} + static void oxp_rgb_queue_fn(struct work_struct *work) { struct oxp_rgb_led *led =3D container_of(to_delayed_work(work), @@ -1597,7 +1924,10 @@ static void oxp_rgb_queue_fn(struct work_struct *wor= k) =20 switch (led->type) { case OXP_RGB_FULL: - oxp_rgb_full_queue(led, oxp_rgb_full_state(led)); + if (cfg->x2_rgb) + oxp_gen_3_rgb_queue(led, led->state); + else + oxp_rgb_full_queue(led, led->state); break; } } @@ -1751,9 +2081,10 @@ static struct quirk_entry quirk_hybrid_mcu =3D { .hybrid_mcu =3D true, }; =20 -static struct quirk_entry quirk_x2_bmap =3D { +static struct quirk_entry quirk_x2 =3D { .bmap_format =3D OXP_BMAP_FORMAT_X2, .bmap_page_3 =3D true, + .x2_rgb =3D true, .cfg_interface_num =3D 2, }; =20 @@ -1788,7 +2119,7 @@ static const struct dmi_system_id oxp_quirk_list[] = =3D { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ONE-NETBOOK"), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ONEXPLAYER 3"), }, - .driver_data =3D &quirk_x2_bmap, + .driver_data =3D &quirk_x2, }, { .ident =3D "OneXPlayer X2 Mini Pro", @@ -1796,7 +2127,7 @@ static const struct dmi_system_id oxp_quirk_list[] = =3D { DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ONE-NETBOOK"), DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "ONEXPLAYER X2Mini PRO"), }, - .driver_data =3D &quirk_x2_bmap, + .driver_data =3D &quirk_x2, }, {}, }; @@ -1868,6 +2199,7 @@ static int oxp_cfg_probe(struct hid_device *hdev, u16= up, return -ENOMEM; =20 cfg->hdev =3D hdev; + cfg->x2_rgb =3D quirks && quirks->x2_rgb; mutex_init(&cfg->cfg_mutex); mutex_init(&cfg->rgb_mutex); spin_lock_init(&cfg->rgb_reply_lock); From nobody Fri Sep 25 17:42:22 2026 Received: from mail-oo2-f42.google.com (mail-oo2-f42.google.com [74.125.231.170]) (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 4512B3AC0F1 for ; Thu, 10 Sep 2026 03:21:43 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.231.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789010507; cv=none; b=ophIzSg8hxNZPWBPchMQni4+RnkFbOSIYPgZcGC1mCAiHiKB9g4NFodHAGkTd8bRwHkWnGE8vL/irKAUdYLW0KjDd7YmhSgpRTqvNK4mq+5uOIdVusrQF+2XjJwXEmS4BQBRccOJVZQgerJ+HrL6vGIaqZ9BTzQTa/HQIRxvOQE= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789010507; c=relaxed/simple; bh=Wk25pDrWZZ8xckY9K4N8F+kJpb59de13eoDp0NRWCAI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=WChZcoxCHi6np0t+40nCQbLZH1zjJNREaXNTfqRmDiVCULpXptRkuyNdE/XVg0Cycj+e8Lve9zxWonlsYbKiB3BSglvvc3AkxEmjzX2He9VMCJyTudikOm/XPB/eDjrC38clAGPjn+Ie4xX9NB3YcZIrxXYlJ+kMxPP0CWfvwco= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com; spf=pass smtp.mailfrom=gmail.com; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b=pxKo/Sog; arc=none smtp.client-ip=74.125.231.170 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=gmail.com Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=gmail.com Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=gmail.com header.i=@gmail.com header.b="pxKo/Sog" Received: by mail-oo2-f42.google.com with SMTP id 46e09a7af769-7f4bdf4f0e7so686420a34.3 for ; Wed, 09 Sep 2026 20:21:43 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20251104; t=1789010502; x=1789615302; darn=vger.kernel.org; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:from:to:cc:subject:date :message-id:reply-to:content-type; bh=hOmOC07Rob7vDzQ8fVisbebB3r+JqdG7WBbz/t4NVWc=; b=pxKo/Sogex9TpyjqSnsfL5A5474RrtiEHwkLJeCRsF9tISQmRc2LyUcCweDXEPz+4Z 5fAh3q+0fKTurut7X6Iuarxluyg8OohC7+coPXSUa9gG7hrFsVq0dGte91eIZeQipTGo cCwmyr59vCOMuAYSTXdaEW3LzIcg9/66daytA8pscL/fG1RPj2nEYLRK7LJ6X8wjt1mN OCn0E01fMnXkWMWv+HBk2I3kj/SU6/WXTWPqy/XoDQa/AowARouNPjyOYwmgXe6f5zvd xL8LNlTII0PrJmGLFeKAc0LjhVdrMamLBH0l6sv2MsPw9FjIgDGbXvWt56kXnHf8vYRi si7w== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1789010502; x=1789615302; h=content-transfer-encoding:mime-version:references:in-reply-to :message-id:date:subject:cc:to:from:x-gm-gg:x-gm-message-state:from :to:cc:subject:date:message-id:reply-to:content-type; bh=hOmOC07Rob7vDzQ8fVisbebB3r+JqdG7WBbz/t4NVWc=; b=Xm11nRVyzxGjzeEOkL//1hLZt5IYBY5ePyfgE69YkViOaGWhIpumjSYMjUputtZlL3 Vh6rOO1MhoVt6fzYd8a5PxpawcgmHuW8KEaRohFWTuFEVPiTF2NcNuPTocnfEW3Vczwy GvpH6/JIX2HipKHXA7fi3alLqRCJjcSEjLjLEQBYg18Fna24Ht1WWU4YD3nwbL3pBseY EP7iLibW9OROvxOyAFTEjw68OjSS8+p17DoVuhf4v4SFYkgTkWiRCMzVLGd3kcZTf7w8 tRn3pntqa4LLfIktEIocMjqPbcgQVYes8kYL7nd9qsz5EVWOHpI6Tz55ml+eMLI0Bw4J uOGw== X-Forwarded-Encrypted: i=1; AKwUvBx1iTDpd1rt/67pAWXCOQ6nOCVxKe2osRz7FXkej+4Xfmt2A64JnZhKr9TiR6V3HPz65/HiTNCNoH4+VDA=@vger.kernel.org X-Gm-Message-State: AFuF++nrT5+l97StDe8GY1QmWiXYdpCatevk30bfb1Wi79LUowBVhbrX kWooQuvd81ntxxc3cUoKi9OfhaY4qNcI6N/8B6LH6mlCSrGRFCtlsQ24 X-Gm-Gg: AYBFou22e0kMYz6kKAICzngYp54tJeiWt2tDF1J0OkBP1lrdrHaMb2In6/JdcFYYlPE 7sRSYxjjbAK/q9JwJ9WON0mZKHWaYgHuXfZD0SUAgmU+DknJkvN+KEbBwo3IvCyjqgLkLcwymeF X4NKLXJKDq5+8vVWLnKtD8OOvmc98ULfNOzdtVHWKYX8w25SwNEyO5xi4JbG6L8DlNxvwRff1OX 7VYuTFMbMHxVHeQVuHzINywPo1VXo7CoL76vFrVefR4fEb2NJPEj9ATBFpce4G5pkUiLsynO0QP xdr8pTq2UMfXXMAn/4kQ4tK6hi7gVIZ+4Y1Q/duxf45CuVPffZ3pJTPq6xchlqpDe1EWPxT0jEU jpPeGT84axSuxPzd2yVE/AQeJJF5zD8ywS4iUmyulLLhx6iKIJqv7Hp8/kk6sOdn6i8tOn7QEKX z0k2R3sHHeEz0IYPL2xmZVN/hJTg3Nf0+/GFSJyWacGg/2dWa4QAPhuzvFdQGPm/szPjoo+73E2 lvlFIe06UNH/8tBzAbTBp/cLB2rDaJatv6NJvKwJbsrUchQjcgyJVM= X-Received: by 2002:a05:6830:82aa:b0:7e9:e1ca:5316 with SMTP id 46e09a7af769-7fb76b4310dmr15524364a34.0.1789010502075; Wed, 09 Sep 2026 20:21:42 -0700 (PDT) Received: from localhost ([76.187.192.44]) by smtp.gmail.com with ESMTPSA id 46e09a7af769-7f9f6da9a49sm19758315a34.13.2026.09.09.20.21.41 (version=TLS1_3 cipher=TLS_CHACHA20_POLY1305_SHA256 bits=256/256); Wed, 09 Sep 2026 20:21:41 -0700 (PDT) From: Andrei Aldea To: Jiri Kosina , Benjamin Tissoires , "Derek J. Clark" Cc: linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, Lee Jones , Pavel Machek , linux-leds@vger.kernel.org, linux-api@vger.kernel.org Subject: [PATCH 15/15] HID: hid-oxp: add X2 auxiliary RGB zones Date: Wed, 9 Sep 2026 22:21:15 -0500 Message-ID: <20260910032115.28669-16-andrei1998@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260910032115.28669-1-andrei1998@gmail.com> References: <20260910032115.28669-1-andrei1998@gmail.com> Precedence: bulk X-Mailing-List: linux-kernel@vger.kernel.org List-Id: List-Subscribe: List-Unsubscribe: MIME-Version: 1.0 Content-Transfer-Encoding: quoted-printable Content-Type: text/plain; charset="utf-8" Add separate multicolor LED devices for the X2 guide button and rear logo using Gen3 zones 5 and 6. Each LED retains independent color, brightness, and monocolor or breathing effect state. Use the common LED array and type-tagged state introduced earlier. Protect auxiliary state snapshots with scoped spinlocks, serialize output with other RGB operations, and retry a failed command once. Do not alter an auxiliary zone until userspace has supplied valid state, and restore valid cached settings after resume. Expose these zones only on the matched ONEXPLAYER 3 and X2 Mini Pro. Physical validation of the latter remains outstanding. Document the auxiliary effects together with the existing joystick-ring effects and add the previously omitted controller, mapping, rumble, and ring-lighting ABI. Add the ABI file to the ONEXPLAYER HID maintainer pattern. Assisted-by: LLM Reviewed-by: Derek J. Clark Signed-off-by: Andrei Aldea --- .../ABI/testing/sysfs-driver-hid-oxp | 196 +++++++++++ MAINTAINERS | 1 + drivers/hid/hid-oxp.c | 305 +++++++++++++++++- 3 files changed, 497 insertions(+), 5 deletions(-) create mode 100644 Documentation/ABI/testing/sysfs-driver-hid-oxp diff --git a/Documentation/ABI/testing/sysfs-driver-hid-oxp b/Documentation= /ABI/testing/sysfs-driver-hid-oxp new file mode 100644 index 0000000..7c97f28 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-driver-hid-oxp @@ -0,0 +1,196 @@ +What: /sys/class/leds/oxp:rgb:joystick_rings/effect +What: /sys/class/leds/oxp:rgb:guide_button/effect +What: /sys/class/leds/oxp:rgb:rear_logo/effect +Date: April 2026 +Contact: Derek J. Clark +Description: Read/write attribute selecting the lighting effect for the LE= D. + + The joystick_rings LED supports the following effect names: + + ``aurora``, ``birthday_cake``, ``flowing_light``, + ``chroma_popping``, ``neon``, ``chroma_breathing``, ``dreamy``, + ``warm_sun``, ``cyberpunk``, ``sea_foam``, ``sunset_afterglow``, + ``colorful``, ``monster_woke``, ``green_breathing``, + ``blue_breathing``, ``yellow_breathing``, ``teal_breathing``, + ``purple_breathing``, ``foggy_haze``, and ``monocolor``. + + The guide_button and rear_logo LEDs support ``monocolor`` and + ``breathing``. These two LEDs were added in August 2026 and are + available on the ONEXPLAYER 3 and ONEXPLAYER X2 Mini Pro. + + The ``monocolor`` effect displays the color selected through + the standard multicolor LED ``multi_intensity`` attribute. + The auxiliary ``breathing`` effect pulses the selected color. + Other joystick ring effects select firmware-defined presets. + The standard brightness and multicolor attributes are documented + in Documentation/ABI/testing/sysfs-class-led and + Documentation/ABI/testing/sysfs-class-led-multicolor. + + Read returns the selected effect name followed by a newline. + The value may reflect cached driver state rather than a live + hardware query. The joystick_rings LED may report ``unknown`` + when no known effect has been recorded; this is not a selectable + effect. Auxiliary effect writes queue the update asynchronously. + +What: /sys/class/leds/oxp:rgb:joystick_rings/effect_index +What: /sys/class/leds/oxp:rgb:guide_button/effect_index +What: /sys/class/leds/oxp:rgb:rear_logo/effect_index +Date: April 2026 +Contact: Derek J. Clark +Description: Read-only list of effect names supported by the corresponding + LED, separated by spaces and terminated by a newline. + + The supported names are described in the effect entry above. + The list is specific to each LED and does not include ``unknown``. + The guide_button and rear_logo attributes were added in August + 2026 and are available on the ONEXPLAYER 3 and ONEXPLAYER X2 + Mini Pro. + +What: /sys/class/leds/oxp:rgb:joystick_rings/enabled +Date: April 2026 +Contact: Derek J. Clark +Description: Read/write enable state for the joystick ring lighting. + + Write ``true`` to enable lighting or ``false`` to disable it. + Only these literal names are accepted, not numeric boolean + values. Read requests lighting status and returns the driver's + enable state as one of these names followed by a newline. + + This attribute is not exposed for guide_button or rear_logo. + +What: /sys/class/leds/oxp:rgb:joystick_rings/enabled_index +Date: April 2026 +Contact: Derek J. Clark +Description: Read-only list of names accepted by enabled. Returns + ``false true`` followed by a newline. + +What: /sys/class/leds/oxp:rgb:joystick_rings/speed +Date: April 2026 +Contact: Derek J. Clark +Description: Read/write firmware animation-speed setting for the joystick + ring lighting. Write a decimal integer from 0 through 9. + These are firmware levels, not a duration or frequency. + + Read requests lighting status and returns the driver's speed + setting as a decimal integer followed by a newline. + This attribute is not exposed for guide_button or rear_logo. + +What: /sys/class/leds/oxp:rgb:joystick_rings/speed_range +Date: April 2026 +Contact: Derek J. Clark +Description: Read-only range of values accepted by speed. Returns ``0-9`` + followed by a newline; both endpoints are inclusive. + +What: /sys/bus/hid/devices/.../gamepad_mode +Date: April 2026 +Contact: Derek J. Clark +Description: Read/write controller mode on the HID configuration interface + with usage page 0xff00 (shared by Gen2 and X2/Gen3 devices). Write one o= f the following names: + + * ``xinput``: normal gamepad input. + * ``debug``: route controller events to a hidraw interface for + input-mapping diagnostics instead of the xpad evdev interface. + + Read returns the driver's cached mode name followed by a newline, + not a firmware query. The initial cached mode is ``xinput``. + Switching to ``xinput`` also reapplies the cached rumble intensity. + + This attribute and the other controller configuration attributes + below are attached directly to the selected HID configuration device's + sysfs directory, not an input device or LED directory. They are + not exposed on Gen1 configuration interfaces. + +What: /sys/bus/hid/devices/.../gamepad_mode_index +Date: April 2026 +Contact: Derek J. Clark +Description: Read-only list of supported controller modes. Returns + ``xinput debug`` followed by a newline. + +What: /sys/bus/hid/devices/.../button_a +What: /sys/bus/hid/devices/.../button_b +What: /sys/bus/hid/devices/.../button_x +What: /sys/bus/hid/devices/.../button_y +What: /sys/bus/hid/devices/.../button_lb +What: /sys/bus/hid/devices/.../button_rb +What: /sys/bus/hid/devices/.../button_lt +What: /sys/bus/hid/devices/.../button_rt +What: /sys/bus/hid/devices/.../button_start +What: /sys/bus/hid/devices/.../button_select +What: /sys/bus/hid/devices/.../button_l3 +What: /sys/bus/hid/devices/.../button_r3 +What: /sys/bus/hid/devices/.../button_d_up +What: /sys/bus/hid/devices/.../button_d_down +What: /sys/bus/hid/devices/.../button_d_left +What: /sys/bus/hid/devices/.../button_d_right +What: /sys/bus/hid/devices/.../button_m1 +What: /sys/bus/hid/devices/.../button_m2 +Date: April 2026 +Contact: Derek J. Clark +Description: Read/write output mapping for the named physical controller + button on a HID configuration device with usage page 0xff00. + + Write one exact, case-sensitive mapping name from + button_mapping_options, such as ``BTN_A`` or ``KEY_F15``. + Numeric key codes, multiple names, and arbitrary key names + are not accepted. + + Read returns the driver's cached mapping name followed by a + newline. Writes update that cache and queue the firmware update + asynchronously; successful writes do not confirm that the + hardware has already applied the mapping. + +What: /sys/bus/hid/devices/.../button_mapping_options +Date: April 2026 +Contact: Derek J. Clark +Description: Read-only list of output mapping names accepted by the + controller button attributes, separated by spaces and terminated by a + newline. All 18 button attributes accept the same list. + + The gamepad mapping names are: + + ``BTN_A``, ``BTN_B``, ``BTN_X``, ``BTN_Y``, ``BTN_LB``, + ``BTN_RB``, ``BTN_LT``, ``BTN_RT``, ``BTN_START``, ``BTN_SELECT``, + ``BTN_L3``, ``BTN_R3``, ``DPAD_UP``, ``DPAD_DOWN``, + ``DPAD_LEFT``, ``DPAD_RIGHT``, ``JOY_L_UP``, ``JOY_L_UP_RIGHT``, + ``JOY_L_RIGHT``, ``JOY_L_DOWN_RIGHT``, ``JOY_L_DOWN``, + ``JOY_L_DOWN_LEFT``, ``JOY_L_LEFT``, ``JOY_L_UP_LEFT``, + ``JOY_R_UP``, ``JOY_R_UP_RIGHT``, ``JOY_R_RIGHT``, + ``JOY_R_DOWN_RIGHT``, ``JOY_R_DOWN``, ``JOY_R_DOWN_LEFT``, + ``JOY_R_LEFT``, ``JOY_R_UP_LEFT``, and ``BTN_GUIDE``. + + Keyboard mapping names are ``KEY_F1`` through ``KEY_F24``. + There are no ``BTN_M1`` or ``BTN_M2`` output mapping names; + button_m1 and button_m2 select from the same options as the + other buttons. + +What: /sys/bus/hid/devices/.../reset_buttons +Date: April 2026 +Contact: Derek J. Clark +Description: Write-only reset of the controller's button mappings. + Write the decimal value ``1`` to restore the driver's default + mappings and send them to the controller. Other values are + rejected. + + Main controls map to their corresponding gamepad functions; + M1 maps to ``KEY_F15`` and M2 to ``KEY_F16``. This resets button + mappings only, not lighting, rumble, or other device settings. + It is not a factory reset. Cached mappings are reset before + the firmware update and are not rolled back if that update fails. + +What: /sys/bus/hid/devices/.../rumble_intensity +Date: April 2026 +Contact: Derek J. Clark +Description: Read/write rumble-intensity level on the HID configuration + device with usage page 0xff00. Write a decimal integer from 0 through 5.= These are + firmware levels, not percentages or values in physical units. + + Read returns the driver's cached level as a decimal integer + followed by a newline. The initial cached value is 5. A write + sends the firmware command before updating the cached level. + +What: /sys/bus/hid/devices/.../rumble_intensity_range +Date: April 2026 +Contact: Derek J. Clark +Description: Read-only range of values accepted by the rumble_intensity + attribute. Returns ``0-5`` followed by a newline; both endpoints + are inclusive. diff --git a/MAINTAINERS b/MAINTAINERS index c392ded..fe5a2f6 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -20381,6 +20381,7 @@ ONEXPLAYER HID DRIVER M: Derek J. Clark L: linux-input@vger.kernel.org S: Maintained +F: Documentation/ABI/testing/sysfs-driver-hid-oxp F: drivers/hid/hid-oxp.c =20 ONEXPLAYER PLATFORM EC DRIVER diff --git a/drivers/hid/hid-oxp.c b/drivers/hid/hid-oxp.c index 9a46602..757cef8 100644 --- a/drivers/hid/hid-oxp.c +++ b/drivers/hid/hid-oxp.c @@ -45,6 +45,7 @@ #define OXP_GET_PROPERTY 0xfc #define OXP_SET_PROPERTY 0xfd #define OXP_EFFECT_MONO_TRUE 0xfe /* actual index for monocolor */ +#define OXP_EFFECT_BREATHING_TRUE 0xf0 =20 #define OXP_DEVICE_ATTR_RW(_name, _group) = \ static ssize_t _name##_store(struct device *dev, \ @@ -358,6 +359,17 @@ struct oxp_gen_3_rgb_color_report { =20 enum oxp_rgb_type { OXP_RGB_FULL, + OXP_RGB_AUX, +}; + +enum oxp_rgb_aux_effect { + OXP_RGB_AUX_EFFECT_MONOCOLOR, + OXP_RGB_AUX_EFFECT_BREATHING, +}; + +static const char *const oxp_rgb_aux_effect_text[] =3D { + [OXP_RGB_AUX_EFFECT_MONOCOLOR] =3D "monocolor", + [OXP_RGB_AUX_EFFECT_BREATHING] =3D "breathing", }; =20 struct oxp_rgb_full_state { @@ -367,18 +379,30 @@ struct oxp_rgb_full_state { u8 speed; }; =20 +struct oxp_rgb_aux_state { + enum led_brightness brightness; + bool valid; + u8 effect; + u8 green; + u8 blue; + u8 red; +}; + struct oxp_rgb_led { struct mc_subled subled_info[3]; struct led_classdev_mc mc_cdev; struct delayed_work work; struct oxp_hid_cfg *cfg; + spinlock_t state_lock; /* protects auxiliary RGB state */ void *state; u8 type; + u8 zone; }; =20 struct oxp_rgb_led_desc { const char *name; u8 type; + u8 zone; }; =20 struct oxp_attr { @@ -417,6 +441,23 @@ static struct oxp_rgb_full_state *oxp_rgb_full_state(s= truct oxp_rgb_led *led) switch (led->type) { case OXP_RGB_FULL: return led->state; + case OXP_RGB_AUX: + return NULL; + } + + return NULL; +} + +static struct oxp_rgb_aux_state *oxp_rgb_aux_state(struct oxp_rgb_led *led) +{ + if (!led) + return NULL; + + switch (led->type) { + case OXP_RGB_FULL: + return NULL; + case OXP_RGB_AUX: + return led->state; } =20 return NULL; @@ -473,6 +514,8 @@ static int oxp_rgb_status_store(struct oxp_rgb_led *led= , u8 enabled, u8 speed, u8 brightness); static int oxp_rgb_effect_set(struct oxp_rgb_led *led, u8 effect); =20 +static void oxp_rgb_aux_restore_locked(struct oxp_rgb_led *led); + static void oxp_rgb_restore(struct oxp_hid_cfg *cfg) { struct oxp_rgb_full_state *state; @@ -510,6 +553,9 @@ static void oxp_rgb_restore(struct oxp_hid_cfg *cfg) "Error: Failed to restore RGB effect: %i\n", ret); break; + case OXP_RGB_AUX: + oxp_rgb_aux_restore_locked(led); + break; } } } @@ -842,6 +888,19 @@ static int oxp_gen_3_rgb_property_out(struct oxp_hid_c= fg *cfg, u8 *data, return first_err; } =20 +static int oxp_gen_3_rgb_zone_property_out(struct oxp_hid_cfg *cfg, + u8 *data, u8 data_size) +{ + int ret; + + ret =3D oxp_gen_3_property_out(cfg, data, data_size); + /* Retry once if the write or its acknowledgment failed. */ + if (ret) + ret =3D oxp_gen_3_property_out(cfg, data, data_size); + + return ret; +} + static void oxp_gen_3_rgb_fill_color(struct oxp_gen_3_rgb_color_report *re= port, u8 command, u8 zone, u8 red, u8 green, u8 blue) @@ -858,6 +917,85 @@ static void oxp_gen_3_rgb_fill_color(struct oxp_gen_3_= rgb_color_report *report, report->final_green =3D green; } =20 +static bool oxp_rgb_aux_snapshot(struct oxp_rgb_led *led, + struct oxp_rgb_aux_state *state) +{ + struct oxp_rgb_aux_state *cached =3D oxp_rgb_aux_state(led); + + if (!cached) + return false; + + scoped_guard(spinlock_irqsave, &led->state_lock) { + *state =3D *cached; + } + + return state->valid; +} + +static int oxp_rgb_aux_apply(struct oxp_rgb_led *led, + const struct oxp_rgb_aux_state *state) +{ + struct oxp_hid_cfg *cfg =3D led->cfg; + unsigned int max_brightness =3D led->mc_cdev.led_cdev.max_brightness; + unsigned int brightness =3D min_t(unsigned int, state->brightness, + max_brightness); + u8 status_data[6] =3D { OXP_SET_PROPERTY, led->zone, 0x02, + OXP_FEAT_DISABLED, 0x05, 0x04 }; + struct oxp_gen_3_rgb_color_report color_report; + u8 command; + u8 green; + u8 blue; + u8 red; + int ret; + + if (brightness) + status_data[3] =3D OXP_FEAT_ENABLED; + + ret =3D oxp_gen_3_rgb_zone_property_out(cfg, status_data, + sizeof(status_data)); + if (ret || !brightness) + return ret; + + /* Scale each RGB channel by brightness, rounding to the nearest integer.= */ + red =3D ((unsigned int)state->red * brightness + max_brightness / 2) / + max_brightness; + green =3D ((unsigned int)state->green * brightness + max_brightness / 2) / + max_brightness; + blue =3D ((unsigned int)state->blue * brightness + max_brightness / 2) / + max_brightness; + + switch (state->effect) { + case OXP_RGB_AUX_EFFECT_MONOCOLOR: + command =3D OXP_EFFECT_MONO_TRUE; + break; + case OXP_RGB_AUX_EFFECT_BREATHING: + command =3D OXP_EFFECT_BREATHING_TRUE; + break; + default: + return -EINVAL; + } + + oxp_gen_3_rgb_fill_color(&color_report, command, led->zone, red, green, + blue); + return oxp_gen_3_rgb_zone_property_out(cfg, (u8 *)&color_report, + sizeof(color_report)); +} + +static void oxp_rgb_aux_restore_locked(struct oxp_rgb_led *led) +{ + struct oxp_hid_cfg *cfg =3D led->cfg; + struct oxp_rgb_aux_state state; + int ret; + + if (!oxp_rgb_aux_snapshot(led, &state)) + return; + + ret =3D oxp_rgb_aux_apply(led, &state); + if (ret) + dev_err(&cfg->hdev->dev, + "Failed to restore RGB zone %#04x: %i\n", led->zone, ret); +} + static ssize_t gamepad_mode_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t count) @@ -1913,6 +2051,25 @@ static void oxp_gen_3_rgb_queue(struct oxp_rgb_led *= led, "Error: Failed to write RGB color: %i\n", ret); } =20 +static void oxp_rgb_aux_queue(struct oxp_rgb_led *led) +{ + struct oxp_hid_cfg *cfg =3D led->cfg; + struct oxp_rgb_aux_state state; + int ret; + + if (!oxp_rgb_aux_snapshot(led, &state)) + return; + + guard(mutex)(&cfg->rgb_mutex); + if (READ_ONCE(cfg->suspended) || READ_ONCE(cfg->removing)) + return; + + ret =3D oxp_rgb_aux_apply(led, &state); + if (ret) + dev_err(led->mc_cdev.led_cdev.dev, + "Failed to write RGB zone %#04x: %i\n", led->zone, ret); +} + static void oxp_rgb_queue_fn(struct work_struct *work) { struct oxp_rgb_led *led =3D container_of(to_delayed_work(work), @@ -1929,6 +2086,9 @@ static void oxp_rgb_queue_fn(struct work_struct *work) else oxp_rgb_full_queue(led, led->state); break; + case OXP_RGB_AUX: + oxp_rgb_aux_queue(led); + break; } } =20 @@ -1937,16 +2097,112 @@ static void oxp_rgb_brightness_set(struct led_clas= sdev *led_cdev, { struct led_classdev_mc *mc_cdev =3D lcdev_to_mccdev(led_cdev); struct oxp_rgb_led *led =3D container_of(mc_cdev, struct oxp_rgb_led, - mc_cdev); + mc_cdev); struct oxp_hid_cfg *cfg =3D led->cfg; + struct oxp_rgb_aux_state *state; =20 - if (READ_ONCE(cfg->suspended) || READ_ONCE(cfg->removing)) + if (READ_ONCE(cfg->removing)) + return; + + switch (led->type) { + case OXP_RGB_FULL: + if (READ_ONCE(cfg->suspended)) + return; + led_cdev->brightness =3D brightness; + break; + case OXP_RGB_AUX: + state =3D led->state; + led_cdev->brightness =3D brightness; + scoped_guard(spinlock_irqsave, &led->state_lock) { + state->brightness =3D brightness; + state->red =3D led->subled_info[0].intensity; + state->green =3D led->subled_info[1].intensity; + state->blue =3D led->subled_info[2].intensity; + state->valid =3D true; + } + if (READ_ONCE(cfg->suspended)) + return; + break; + default: return; + } =20 - led_cdev->brightness =3D brightness; mod_delayed_work(system_dfl_wq, &led->work, msecs_to_jiffies(50)); } =20 +static ssize_t oxp_rgb_aux_effect_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct oxp_rgb_led *led =3D oxp_rgb_led_from_dev(dev); + struct oxp_hid_cfg *cfg =3D led->cfg; + struct oxp_rgb_aux_state *state =3D oxp_rgb_aux_state(led); + int ret; + + if (!state || READ_ONCE(cfg->removing)) + return -ENODEV; + + ret =3D sysfs_match_string(oxp_rgb_aux_effect_text, buf); + if (ret < 0) + return ret; + + scoped_guard(spinlock_irqsave, &led->state_lock) { + state->effect =3D ret; + state->valid =3D true; + } + + if (!READ_ONCE(cfg->suspended) && !READ_ONCE(cfg->removing)) + mod_delayed_work(system_dfl_wq, &led->work, + msecs_to_jiffies(50)); + + return count; +} + +static ssize_t oxp_rgb_aux_effect_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct oxp_rgb_led *led =3D oxp_rgb_led_from_dev(dev); + struct oxp_hid_cfg *cfg =3D led->cfg; + struct oxp_rgb_aux_state state; + + if (!oxp_rgb_aux_state(led) || READ_ONCE(cfg->removing)) + return -ENODEV; + + oxp_rgb_aux_snapshot(led, &state); + if (state.effect >=3D ARRAY_SIZE(oxp_rgb_aux_effect_text)) + return -EINVAL; + + return sysfs_emit(buf, "%s\n", oxp_rgb_aux_effect_text[state.effect]); +} +static DEVICE_ATTR_RW_NAMED(oxp_rgb_aux_effect, "effect"); + +static ssize_t oxp_rgb_aux_effect_index_show(struct device *dev, + struct device_attribute *attr, + char *buf) +{ + int count =3D 0; + int i; + + for (i =3D 0; i < ARRAY_SIZE(oxp_rgb_aux_effect_text); i++) + count +=3D sysfs_emit_at(buf, count, "%s ", + oxp_rgb_aux_effect_text[i]); + if (count) + buf[count - 1] =3D '\n'; + + return count; +} +static DEVICE_ATTR_RO_NAMED(oxp_rgb_aux_effect_index, "effect_index"); + +static struct attribute *oxp_rgb_aux_attrs[] =3D { + &dev_attr_oxp_rgb_aux_effect.attr, + &dev_attr_oxp_rgb_aux_effect_index.attr, + NULL, +}; + +static const struct attribute_group oxp_rgb_aux_attr_group =3D { + .attrs =3D oxp_rgb_aux_attrs, +}; + static struct attribute *oxp_rgb_attrs[] =3D { &dev_attr_effect.attr, &dev_attr_effect_index.attr, @@ -1966,12 +2222,23 @@ static const struct oxp_rgb_led_desc oxp_rgb_led_de= scs[] =3D { .name =3D "oxp:rgb:joystick_rings", .type =3D OXP_RGB_FULL, }, + { + .name =3D "oxp:rgb:guide_button", + .type =3D OXP_RGB_AUX, + .zone =3D 0x05, + }, + { + .name =3D "oxp:rgb:rear_logo", + .type =3D OXP_RGB_AUX, + .zone =3D 0x06, + }, }; =20 static int oxp_rgb_led_init(struct oxp_hid_cfg *cfg, struct oxp_rgb_led *l= ed, const struct oxp_rgb_led_desc *desc) { struct oxp_rgb_full_state *full_state; + struct oxp_rgb_aux_state *aux_state; struct hid_device *hdev =3D cfg->hdev; u8 green; u8 blue; @@ -1979,6 +2246,7 @@ static int oxp_rgb_led_init(struct oxp_hid_cfg *cfg, = struct oxp_rgb_led *led, =20 led->cfg =3D cfg; led->type =3D desc->type; + led->zone =3D desc->zone; =20 switch (led->type) { case OXP_RGB_FULL: @@ -1992,6 +2260,20 @@ static int oxp_rgb_led_init(struct oxp_hid_cfg *cfg,= struct oxp_rgb_led *led, green =3D 0x22; blue =3D 0x99; break; + case OXP_RGB_AUX: + aux_state =3D devm_kzalloc(&hdev->dev, sizeof(*aux_state), + GFP_KERNEL); + if (!aux_state) + return -ENOMEM; + aux_state->red =3D 0xff; + aux_state->green =3D 0xff; + aux_state->blue =3D 0xff; + aux_state->effect =3D OXP_RGB_AUX_EFFECT_MONOCOLOR; + led->state =3D aux_state; + red =3D 0xff; + green =3D 0xff; + blue =3D 0xff; + break; default: return -EINVAL; } @@ -2020,6 +2302,7 @@ static int oxp_rgb_led_init(struct oxp_hid_cfg *cfg, = struct oxp_rgb_led *led, led->mc_cdev.led_cdev.brightness_set =3D oxp_rgb_brightness_set; led->mc_cdev.num_colors =3D ARRAY_SIZE(led->subled_info); led->mc_cdev.subled_info =3D led->subled_info; + spin_lock_init(&led->state_lock); INIT_DELAYED_WORK(&led->work, oxp_rgb_queue_fn); =20 return 0; @@ -2027,7 +2310,8 @@ static int oxp_rgb_led_init(struct oxp_hid_cfg *cfg, = struct oxp_rgb_led *led, =20 static int oxp_rgb_leds_register(struct oxp_hid_cfg *cfg) { - int led_count =3D ARRAY_SIZE(oxp_rgb_led_descs); + int led_count =3D cfg->x2_rgb ? ARRAY_SIZE(oxp_rgb_led_descs) : 1; + const struct attribute_group *attr_group; struct hid_device *hdev =3D cfg->hdev; struct oxp_rgb_led *led; int ret; @@ -2051,8 +2335,19 @@ static int oxp_rgb_leds_register(struct oxp_hid_cfg = *cfg) return dev_err_probe(&hdev->dev, ret, "Failed to create RGB device\n"); =20 + switch (led->type) { + case OXP_RGB_FULL: + attr_group =3D &oxp_rgb_attr_group; + break; + case OXP_RGB_AUX: + attr_group =3D &oxp_rgb_aux_attr_group; + break; + default: + return -EINVAL; + } + ret =3D devm_device_add_group(led->mc_cdev.led_cdev.dev, - &oxp_rgb_attr_group); + attr_group); if (ret) return dev_err_probe(led->mc_cdev.led_cdev.dev, ret, "Failed to create RGB configuration attributes\n");