From nobody Sat Apr 4 01:55:42 2026 Received: from mail-dy1-f170.google.com (mail-dy1-f170.google.com [74.125.82.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 D09AE34F46B for ; Sun, 22 Mar 2026 03:16:20 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=74.125.82.170 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774149383; cv=none; b=UgdlgIguHu5Fce5lg0eJaJC1EKqx9X8ZtrJCApw9gFpFgnhBXMCLYUBq8SbzkJbKFxKJOGszBf8BxqaEWdEIoBa148aMwsXrKo9FZqL5qEqhhzcpx05NXkybQ3bnh1IdnubWLJ9OF4p6A7l/brypKvq7BIZ9pVU2jqRtsi5chN4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1774149383; c=relaxed/simple; bh=8gNYXpQGmKI1m3W5oqjVsx8k92Qrt1pu/7hbhyiLEXc=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=IMK8snHXDZhAczVVZBaT4PjQdeYjJeJydgWkNcWym658q4sSoiizVDBLC6zlOec78ApRoS+tseXO4YVEI0P4JfMs0YBLR4YLy/ENHCPKltHanSx+rQ1AsERbnjk8SkgzeQ7KqS66rG0rIdw8x/ozAcUS0zMrcmnoLSlRhezLRs8= 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=bKKwtJyw; arc=none smtp.client-ip=74.125.82.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="bKKwtJyw" Received: by mail-dy1-f170.google.com with SMTP id 5a478bee46e88-2c1092cc08cso4774744eec.1 for ; Sat, 21 Mar 2026 20:16:20 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20230601; t=1774149380; x=1774754180; 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; bh=CpOppp7pshHB2mWvGps3l7V3xeQrRabavfMVMwqvkLM=; b=bKKwtJywVuCbrGQa/bXHCVoNm2DuyA075XhDNf2Yz6sEctOU3pErCvMAz4vf62lg1Z lFmFN3DOB8b1RO9TTrRMrMZgvFgJlQKAxyJvcdMot8Sf2xrkL/6boCmHNLSTLRW1Se8d pPUwsHN0Na1WwVW344fROt5k6honayFEbhwNNeDO6pQCFBA39M0QfjwciQYb22gQ/mGk 8r1AcxAQ/7SQ2MoiYfwrExl8zgpr12VOg+OlT+bjNAswp+3+SMulFHM5QPoHF9ddcXum XrxOAQd2zUsnLx2Pig+al5yPHwgQPG2HuTC1ln/dpeuwtwh1r6AGXCq5bpCT8pRhZmh0 YB6A== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20251104; t=1774149380; x=1774754180; 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; bh=CpOppp7pshHB2mWvGps3l7V3xeQrRabavfMVMwqvkLM=; b=LotmYgNa2zOjCGtpAjBvDRkjKMLTiQZ9t6WQMU0sLwlhYCAlEzjFr4Ml58mhecI6fV 3K08UFzR4S4cIXp+TIZUCMiIHjddfSR5hIStMjx62DvSik1fm/4CYOHAofTYtelhqTGw rgutsm+LLl7TT8hT1uFGdcQtX/8uCNScC1FHl56oikCTKtJvJySqNh32fHSQnkw5K08m Zzvjuf6QbjKRD7+InmOUINmk4Cn04dNy3mFbXGGqq8lz1CFkGA0jTeuSw5/cU6Xm7xl4 RrV1IgpVy9/OK4Zh4b5aMzLYL8nESiOQPjurs4YwyKG1YB0ECIIOerPznFh9/GlEvHfm T94A== X-Forwarded-Encrypted: i=1; AJvYcCU5mxzDqKYQIv5EIqsELxjLqo2vSkF8DkBZcX6RNbGcFuk2eX8Zh/2tlJVDbKwgqlGFD2o+zDW1Ho+S9UM=@vger.kernel.org X-Gm-Message-State: AOJu0Yz2w6AXO2QF5EjZ0omOjiVMkTcIrPKth1Zho6uM1edST6ecTtMN POqblLWqGCNym5Nb+jfb2zgzEZ65E5Sc3lw4h0HDVu5WDTJsftpp2t7j X-Gm-Gg: ATEYQzy+7bUSQi2WlqjqILYX2RlTKXFNVFbxx4hZCYd+diRT2KqaisAen2WrhFz3TY3 9IfIkO0xnTIQWUiCU9rxU1MjDmaceLonCvO+YjKkn73lFkNElLQ5hjTQuoFwyJOdOz8Bzl0tAFr mAIhsr56JvAccAn7B+L97PDVE7PoLeJDMeeaXRtyin17GRG7MerA6qkJw7xIYRah73kvUSwmiJB 3FVuy1wjFdIAFvSLXbVZA+uoFoBomZRlMQ4zQyOv0esNIzcfCBQyfDOUsMa76GplPIrLQrS7sn7 9moblQRZD2wb9Hzc1gClbLxh77fjvYITEQZqzFY0aWVFtaB5DaRBuWlsmvFwdMYR4L/0ebMdwPZ DakRyXJXXTNHbz54kbN18NNter5jhV+fKdkpgB7Z+0bhH9ak/bzykasneyaFGjCb/yVYhGBVML2 9DGDzuH9Zu6y5sQvexnjCCCDIxbPrT6mh5CQbpZLFFK/fGWCoafB6Y0DYvGam5FV6wVZrw6yHjg HKX/7m2yPhzbzQ= X-Received: by 2002:a05:7300:e2cc:b0:2c1:27c:75a6 with SMTP id 5a478bee46e88-2c1095fbaa9mr3769234eec.10.1774149379723; Sat, 21 Mar 2026 20:16:19 -0700 (PDT) Received: from lappy (108-228-232-20.lightspeed.sndgca.sbcglobal.net. [108.228.232.20]) by smtp.gmail.com with ESMTPSA id 5a478bee46e88-2c10b31bef1sm11220460eec.26.2026.03.21.20.16.19 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Sat, 21 Mar 2026 20:16:19 -0700 (PDT) From: "Derek J. Clark" To: Jiri Kosina , Benjamin Tissoires Cc: "Pierre-Loup A . Griffais" , Lambert Fan , "Derek J . Clark" , linux-input@vger.kernel.org, linux-doc@vger.kernel.org, linux-kernel@vger.kernel.org Subject: [PATCH 2/4] HID: hid-oxp: Add Second Generation RGB Control Date: Sun, 22 Mar 2026 03:16:13 +0000 Message-ID: <20260322031615.1524307-3-derekjohn.clark@gmail.com> X-Mailer: git-send-email 2.53.0 In-Reply-To: <20260322031615.1524307-1-derekjohn.clark@gmail.com> References: <20260322031615.1524307-1-derekjohn.clark@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" Adds support for the second generation of RGB Control for OneXPlayer devices. The interface mirrors the first generation, with some differences to how messages are formatted. Signed-off-by: Derek J. Clark --- drivers/hid/hid-ids.h | 3 ++ drivers/hid/hid-oxp.c | 96 +++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 99 insertions(+) diff --git a/drivers/hid/hid-ids.h b/drivers/hid/hid-ids.h index 8b272d1ab9ba..b33782ba6556 100644 --- a/drivers/hid/hid-ids.h +++ b/drivers/hid/hid-ids.h @@ -1105,6 +1105,9 @@ #define USB_VENDOR_ID_CRSC 0x1a2c #define USB_DEVICE_ID_ONEXPLAYER_GEN1 0xb001 =20 +#define USB_VENDOR_ID_WCH 0x1a86 +#define USB_DEVICE_ID_ONEXPLAYER_GEN2 0xfe00 + #define USB_VENDOR_ID_ONTRAK 0x0a07 #define USB_DEVICE_ID_ONTRAK_ADU100 0x0064 =20 diff --git a/drivers/hid/hid-oxp.c b/drivers/hid/hid-oxp.c index 391de2798320..587e0d57c85f 100644 --- a/drivers/hid/hid-oxp.c +++ b/drivers/hid/hid-oxp.c @@ -24,12 +24,15 @@ #define OXP_PACKET_SIZE 64 =20 #define GEN1_MESSAGE_ID 0xff +#define GEN2_MESSAGE_ID 0x3f =20 #define GEN1_USAGE_PAGE 0xff01 +#define GEN2_USAGE_PAGE 0xff00 =20 enum oxp_function_index { OXP_FID_GEN1_RGB_SET =3D 0x07, OXP_FID_GEN1_RGB_REPLY =3D 0x0f, + OXP_FID_GEN2_RGB_EVENT =3D 0xb8, }; =20 static struct oxp_hid_cfg { @@ -121,6 +124,22 @@ struct oxp_gen_1_rgb_report { u8 blue; } __packed; =20 +struct oxp_gen_2_rgb_report { + u8 report_id; + u8 header_id; + u8 padding_2; + u8 message_id; + u8 padding_4[2]; + u8 enabled; + u8 speed; + u8 brightness; + u8 red; + u8 green; + u8 blue; + u8 padding_12[3]; + u8 effect; +} __packed; + static u16 get_usage_page(struct hid_device *hdev) { return hdev->collection[0].usage >> 16; @@ -162,6 +181,45 @@ static int oxp_hid_raw_event_gen_1(struct hid_device *= hdev, return 0; } =20 +static int oxp_hid_raw_event_gen_2(struct hid_device *hdev, + struct hid_report *report, u8 *data, + int size) +{ + struct led_classdev_mc *led_mc =3D drvdata.led_mc; + struct oxp_gen_2_rgb_report *rgb_rep; + + if (data[0] !=3D OXP_FID_GEN2_RGB_EVENT) + return 0; + + if (data[3] !=3D OXP_GET_PROPERTY) + return 0; + + rgb_rep =3D (struct oxp_gen_2_rgb_report *)data; + /* 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 : + rgb_rep->effect; + drvdata.rgb_speed =3D rgb_rep->speed; + 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; + /* 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, + * our brightness will always look like 100%. Use the last set value to + * 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; + + return 0; +} + static int oxp_hid_raw_event(struct hid_device *hdev, struct hid_report *r= eport, u8 *data, int size) { @@ -172,6 +230,8 @@ static int oxp_hid_raw_event(struct hid_device *hdev, s= truct hid_report *report, switch (up) { case GEN1_USAGE_PAGE: return oxp_hid_raw_event_gen_1(hdev, report, data, size); + case GEN2_USAGE_PAGE: + return oxp_hid_raw_event_gen_2(hdev, report, data, size); default: break; } @@ -217,6 +277,18 @@ static int oxp_gen_1_property_out(enum oxp_function_in= dex fid, u8 *data, return mcu_property_out(header, header_size, data, data_size, NULL, 0); } =20 +static int oxp_gen_2_property_out(enum oxp_function_index fid, u8 *data, + u8 data_size) +{ + u8 header[] =3D { fid, GEN2_MESSAGE_ID, 0x01 }; + u8 footer[] =3D { GEN2_MESSAGE_ID, fid }; + size_t header_size =3D ARRAY_SIZE(header); + size_t footer_size =3D ARRAY_SIZE(footer); + + return mcu_property_out(header, header_size, data, data_size, footer, + footer_size); +} + static int oxp_rgb_status_store(u8 enabled, u8 speed, u8 brightness) { u16 up =3D get_usage_page(drvdata.hdev); @@ -231,6 +303,11 @@ static int oxp_rgb_status_store(u8 enabled, u8 speed, = u8 brightness) if (drvdata.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); + 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) + data[5] =3D 0x04; + return oxp_gen_2_property_out(OXP_FID_GEN2_RGB_EVENT, data, 6); default: return -ENODEV; } @@ -245,6 +322,9 @@ static ssize_t oxp_rgb_status_show(void) case GEN1_USAGE_PAGE: data =3D (u8[1]) { OXP_GET_PROPERTY }; return oxp_gen_1_property_out(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_RGB_EVENT, data, 3); default: return -ENODEV; } @@ -275,6 +355,16 @@ static int oxp_rgb_color_set(void) data[3 * i + 3] =3D blue; } return oxp_gen_1_property_out(OXP_FID_GEN1_RGB_SET, data, size); + case GEN2_USAGE_PAGE: + size =3D 57; + data =3D (u8[57]) { OXP_EFFECT_MONO_TRUE, 0x00, 0x02 }; + + for (i =3D 1; i < size / 3; i++) { + data[3 * i] =3D red; + data[3 * i + 1] =3D green; + data[3 * i + 2] =3D blue; + } + return oxp_gen_2_property_out(OXP_FID_GEN2_RGB_EVENT, data, size); default: return -ENODEV; } @@ -311,6 +401,10 @@ static int oxp_rgb_effect_set(u8 effect) data =3D (u8[1]) { effect }; ret =3D oxp_gen_1_property_out(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_RGB_EVENT, data, 3); + break; default: ret =3D -ENODEV; } @@ -614,6 +708,7 @@ static int oxp_hid_probe(struct hid_device *hdev, =20 switch (up) { case GEN1_USAGE_PAGE: + case GEN2_USAGE_PAGE: ret =3D oxp_cfg_probe(hdev, up); if (ret) { hid_hw_close(hdev); @@ -634,6 +729,7 @@ static void oxp_hid_remove(struct hid_device *hdev) =20 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) }, {} }; =20 --=20 2.53.0