From nobody Fri Sep 25 09:20:34 2026 Received: from mail-43172.protonmail.ch (mail-43172.protonmail.ch [185.70.43.172]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DA0453B27D1; Mon, 14 Sep 2026 23:37:51 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.70.43.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789429075; cv=none; b=vESbIYz1kwmuSXhzaPfoeo04VNe6LKu6DPQ6FFv0e1Hc/Cvrncnmr2YlQSI6VpNQfwCkfQn4bnbu2eHjwZz4Z/sMfThnSYhL9jPFJr1DO+J5FdRLIUIawpEE7AAS7pL3bolWlBFjo1SaF5ixH5NkIfkNmckjEhZnWVtcH8Crkl4= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789429075; c=relaxed/simple; bh=73dEZSYKQyrnCp6rva3qeY6ZIPnE2gMcsRY7zPXy3mo=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=q7aOpp1Yo6ikcUzyG0M580AXPuy4fhbkkQ/csdna04n/LCMIWUV7Zc9DYWqiq+ww2/7Oz8IFvGQ6UDcKirasdpGXGiBrZvytsa+1KbLEVdfMU8tEjvtw0OYI9GgAkEbv+PPbLE6n5h90KAj/y8U/1dD/GypCjSAtBYiNmn3TFxs= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=vasily.cc; spf=pass smtp.mailfrom=vasily.cc; dkim=pass (2048-bit key) header.d=vasily.cc header.i=@vasily.cc header.b=rFcH7POk; arc=none smtp.client-ip=185.70.43.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=vasily.cc Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=vasily.cc Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=vasily.cc header.i=@vasily.cc header.b="rFcH7POk" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=vasily.cc; s=protonmail2; t=1789429069; x=1789688269; bh=afo39gMY1xRYx9X5Pi1btnvze6/snqsc7LiHy0/rX5w=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References:From:To: Cc:Date:Subject:Reply-To:Feedback-ID:Message-ID:BIMI-Selector; b=rFcH7POknqLR1leSHsIGj2tHj6lBtpQXFYPrGnONAofETHbu9JyO46f76200gMBxC X45m7e6C89Pe/7JwimjHLcRpVvjvhX07wEUcnSlRat1CAKDV6J0QnacWE69/ybu0tT r9kTup1HlWUrQuYK5pUn3h0MptGszmmJDTDAR8avlR9GaawvFcMdb3IhZvm5u0IMNF NfmKrs+2Oxpipu10O9QVAwS67+Jp7bB6IXIv9GzzGE8zd1DanHajG8ECsB+qj6lz65 xeBLYLxpXzHC9F1aDSDsZvkCD8E/+OHAHFnrYQGtkKv48hamz+k10AnwGAdqhpYDLE amLtxYAJ5Hz0A== X-Pm-Submission-Id: 4hkM5Q6wxDz1DDX8 From: Vas Zayarskiy To: linux@roeck-us.net, Aleksa Savic , Jack Doan , Jiri Kosina , Benjamin Tissoires Cc: linux-hwmon@vger.kernel.org, linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Jonathan Corbet , Shuah Khan , Randy Dunlap Subject: [PATCH v3 1/4] HID: generic: Respect specialized drivers match callbacks Date: Tue, 15 Sep 2026 02:37:25 +0300 Message-ID: <20260914233728.22725-2-contact@vasily.cc> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260914233728.22725-1-contact@vasily.cc> References: <20260914233728.22725-1-contact@vasily.cc> 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 driver can reject an interface in its match callback even when its ID table matches. hid-generic currently yields based on the ID table alone, leaving such an interface without a driver. Consult the callback after matching the ID table so hid-generic can keep interfaces declined by specialized drivers. This allows the AMPINEL hwmon driver to leave the separate keyboard interface to hid-generic. The same callback check was proposed previously for Logitech devices: Link: https://lkml.iu.edu/hypermail/linux/kernel/2212.0/06724.html Assisted-by: LLM sparse Signed-off-by: Vas Zayarskiy --- drivers/hid/hid-generic.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/drivers/hid/hid-generic.c b/drivers/hid/hid-generic.c index c2de91674..ebc38ae76 100644 --- a/drivers/hid/hid-generic.c +++ b/drivers/hid/hid-generic.c @@ -31,7 +31,13 @@ static int __check_hid_generic(struct device_driver *drv= , void *data) if (hdrv =3D=3D &hid_generic) return 0; =20 - return hid_match_device(hdev, hdrv) !=3D NULL; + if (!hid_match_device(hdev, hdrv)) + return 0; + + if (hdrv->match) + return hdrv->match(hdev, false); + + return 1; } =20 static bool hid_generic_match(struct hid_device *hdev, From nobody Fri Sep 25 09:20:34 2026 Received: from mail-244107.protonmail.ch (mail-244107.protonmail.ch [109.224.244.107]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 14B923B38B5; Mon, 14 Sep 2026 23:37:53 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=109.224.244.107 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789429078; cv=none; b=IWsaXvHYTJAMQWbsPoOOyYl7O7rhLlll+nleGUxY/1VVT30LTxJdadFwE5UvPXCwvsigg50Dj1kKgxP/m6CgkG+1Zeur5/fUDtE2wxBffyV/2M72GAjGKWOHgwXYsSHJwEBZMQwZNtMH5IQQvXFUW3fiaAtN9s8t40L6veR/XQI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789429078; c=relaxed/simple; bh=/piCQHWynLJMYlEKRDmxYhMXbGX5DC4ax41GYe7gL/Y=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=l6M0V3c1r8ciu0cMYg18NJmomkrSHYDt1LJEtoc/L7kwdvNy8/yHozTnY96ZiOrG7XP7aVtthWLhoCP7kr/izj7xTCTY5wkUGOH1uqbgaAs5Lf1HQJolXyzFS3SNhN4u2UsyBcHDrF1umof4iJ9nSQYp49Jne6QQWtrrJo3OVDM= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=vasily.cc; spf=pass smtp.mailfrom=vasily.cc; dkim=pass (2048-bit key) header.d=vasily.cc header.i=@vasily.cc header.b=GiAsiS1Z; arc=none smtp.client-ip=109.224.244.107 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=vasily.cc Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=vasily.cc Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=vasily.cc header.i=@vasily.cc header.b="GiAsiS1Z" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=vasily.cc; s=protonmail2; t=1789429071; x=1789688271; bh=XYDhupGIvX56tzutwpJ8Da0yVLb5FcjQ1xOgf2r+n1w=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References:From:To: Cc:Date:Subject:Reply-To:Feedback-ID:Message-ID:BIMI-Selector; b=GiAsiS1Z4NWedVAsxKfantIxopq/kqpEnktHNCGm+vlLU3qmBSbpe9tKSTuRtcNdf Wgwgfr1zcAZDcCcdxW2GfVggd4mRdmCDyELt0585henTi2rX235+0UI7VYKmEomoKl +eymGYD0B3KfnS0Akf7edxREuJef8S9lQK7qFadue0+sKyb9f2x9JRwLpH+9YTaYTV FdSQ58Jo/KG59GAhbHtbt3zs2R8z7wI+DiTFbj46bPaoBkftmM67MXS9R42NgfLKhT SboPrJT8xkWvVFY4n1CPKWP6PvdcDOcnkmTTlukwfoMK4Ngh5BoakhZooPOAUqiaIX LNTqNInrmsqGw== X-Pm-Submission-Id: 4hkM5S5DLtz1DDXF From: Vas Zayarskiy To: linux@roeck-us.net, Aleksa Savic , Jack Doan , Jiri Kosina , Benjamin Tissoires Cc: linux-hwmon@vger.kernel.org, linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Jonathan Corbet , Shuah Khan , Randy Dunlap Subject: [PATCH v3 2/4] hwmon: (aquacomputer_d5next) Avoid truncating scaled sensor readings Date: Tue, 15 Sep 2026 02:37:26 +0300 Message-ID: <20260914233728.22725-3-contact@vasily.cc> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260914233728.22725-1-contact@vasily.cc> References: <20260914233728.22725-1-contact@vasily.cc> 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" High Flow Next power is reported in watts. Multiplying the 16-bit value by one million can overflow both the signed intermediate and the u32 cache. Store power in long, perform the conversion in u64, and clamp to LONG_MAX. The other existing power conversions fit in a signed 32-bit value. Return ENODATA for unavailable power instead of exposing the stored error as a reading. Widen the shared current cache to u32. The Aquastream XT calibration can exceed 65535 mA; clamp its negative calibration results to zero before assigning to the unsigned cache. This also permits devices with scaled aggregate currents to store milliamperes directly. Assisted-by: LLM sparse Signed-off-by: Vas Zayarskiy --- drivers/hwmon/aquacomputer_d5next.c | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/drivers/hwmon/aquacomputer_d5next.c b/drivers/hwmon/aquacomput= er_d5next.c index 1ca70e726..9178ac4cb 100644 --- a/drivers/hwmon/aquacomputer_d5next.c +++ b/drivers/hwmon/aquacomputer_d5next.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -599,9 +600,9 @@ struct aqc_data { u32 speed_input_min[1]; u32 speed_input_target[1]; u32 speed_input_max[1]; - u32 power_input[8]; + long power_input[8]; u16 voltage_input[8]; - u16 current_input[8]; + u32 current_input[8]; =20 /* Label values */ const char *const *temp_label; @@ -976,7 +977,7 @@ static int aqc_legacy_read(struct aqc_data *priv) =20 /* Calculation derived from linear regression */ sensor_value =3D get_unaligned_le16(priv->buffer + AQUASTREAMXT_PUMP_CUR= R_OFFSET); - priv->current_input[0] =3D DIV_ROUND_CLOSEST(sensor_value * 176, 100) - = 52; + priv->current_input[0] =3D max(DIV_ROUND_CLOSEST(sensor_value * 176, 100= ) - 52, 0); =20 sensor_value =3D get_unaligned_le16(priv->buffer + AQUASTREAMXT_PUMP_VOL= TAGE_OFFSET); priv->voltage_input[0] =3D DIV_ROUND_CLOSEST(sensor_value * 1000, 61); @@ -1070,6 +1071,8 @@ static int aqc_read(struct device *dev, enum hwmon_se= nsor_types type, u32 attr, } break; case hwmon_power: + if (priv->power_input[channel] =3D=3D -ENODATA) + return -ENODATA; *val =3D priv->power_input[channel]; break; case hwmon_pwm: @@ -1426,7 +1429,8 @@ static int aqc_raw_event(struct hid_device *hdev, str= uct hid_report *report, u8 priv->power_input[0] =3D -ENODATA; else priv->power_input[0] =3D - get_unaligned_be16(data + HIGHFLOWNEXT_POWER) * 1000000; + min_t(u64, get_unaligned_be16(data + HIGHFLOWNEXT_POWER) * 1000000U= LL, + LONG_MAX); =20 priv->voltage_input[0] =3D get_unaligned_be16(data + HIGHFLOWNEXT_5V_VOL= TAGE) * 10; priv->voltage_input[1] =3D From nobody Fri Sep 25 09:20:34 2026 Received: from mail-43172.protonmail.ch (mail-43172.protonmail.ch [185.70.43.172]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id 7AE893B3BE7; Mon, 14 Sep 2026 23:37:54 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=185.70.43.172 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789429081; cv=none; b=urIfKAIBUAFYB5FX7Am+TtJ3aA4o4UD7pXsEruLDQ46MgkjCnlt1TNw0mzAORiauBl+LBdK1QSH8IKuYm+GD2HDE5LC9ik43IRChlwU0vs8lWJ/uko7TZ7GFu8NbwCTJoTBGT/Iy37Nz17ExFJlH+LUEXoS57f2pU5W6Djqq2Rg= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789429081; c=relaxed/simple; bh=UNFThLNzlgQn3xGsJH+W8fOTAc4yA9tzNHzeyyZNtG8=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=RgcN7BUFu3jZzuFCF9IXK1Z49g2pyL7EJbtw1sW/RGFTFJZUrXBglcxi+o6F3oqXTmm0+KkF81WPZY1DSkOAMKbnH50nmbIKvAvkwc28CHeghALqTUzDRek5Z1Vo47eVITGr/E6uAXPB5RaxOF0H6EQgjM2gZhxpMQAbGmBJF8o= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=vasily.cc; spf=pass smtp.mailfrom=vasily.cc; dkim=pass (2048-bit key) header.d=vasily.cc header.i=@vasily.cc header.b=NrUN4bkS; arc=none smtp.client-ip=185.70.43.172 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=vasily.cc Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=vasily.cc Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=vasily.cc header.i=@vasily.cc header.b="NrUN4bkS" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=vasily.cc; s=protonmail2; t=1789429072; x=1789688272; bh=4tZj5Jn525QkA17zwkfjXmztpwCqZhcxjdiG+hyLmBM=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References:From:To: Cc:Date:Subject:Reply-To:Feedback-ID:Message-ID:BIMI-Selector; b=NrUN4bkSpGQmbk0q5mJWBGEUR3FTs4IG4cmmfIy8JFQ5dYMCPWD6M/tmHhbtaErOf pRNZmq79QAPVhUIew2K2wQjGCwsnfFMOLItHylxYayMoikjqCHiJxxrC/MGBXveB13 bIIHRwbr+pogcB0LhMi5uTbyzuF/YdEi5eavHess1r6SXAMjUwk4ZT7QA/QO2Kpi5J B2jiC2MANlGWAHd2CJuFGk61AXIXxf0HcXgKZ3F/lnCCYXWysShU6OzP5qE2VF7Y3V qe5DS+UndK3FKG3JKBKCJg3jF6OfZWhyiJMzr813VZphEM1fvj2RcSSAa4hD55ul0E fTPWKRz/hjfaQ== X-Pm-Submission-Id: 4hkM5V46g4z1DDKv From: Vas Zayarskiy To: linux@roeck-us.net, Aleksa Savic , Jack Doan , Jiri Kosina , Benjamin Tissoires Cc: linux-hwmon@vger.kernel.org, linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Jonathan Corbet , Shuah Khan , Randy Dunlap Subject: [PATCH v3 3/4] hwmon: (aquacomputer_d5next) Expire the initial sensor cache Date: Tue, 15 Sep 2026 02:37:27 +0300 Message-ID: <20260914233728.22725-4-contact@vasily.cc> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260914233728.22725-1-contact@vasily.cc> References: <20260914233728.22725-1-contact@vasily.cc> 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 initial update timestamp makes the cache expire at the current jiffy. Since aqc_read() uses time_after(), a read in that same jiffy can expose zero-initialized data or skip the first legacy report request. Initialize the timestamp one tick earlier so the cache starts expired for every supported device. Assisted-by: LLM sparse Signed-off-by: Vas Zayarskiy --- drivers/hwmon/aquacomputer_d5next.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/hwmon/aquacomputer_d5next.c b/drivers/hwmon/aquacomput= er_d5next.c index 9178ac4cb..b58f5b5c9 100644 --- a/drivers/hwmon/aquacomputer_d5next.c +++ b/drivers/hwmon/aquacomputer_d5next.c @@ -1531,7 +1531,7 @@ static int aqc_probe(struct hid_device *hdev, const s= truct hid_device_id *id) priv->hdev =3D hdev; hid_set_drvdata(hdev, priv); =20 - priv->updated =3D jiffies - STATUS_UPDATE_INTERVAL; + priv->updated =3D jiffies - STATUS_UPDATE_INTERVAL - 1; =20 ret =3D hid_parse(hdev); if (ret) From nobody Fri Sep 25 09:20:34 2026 Received: from mail-244106.protonmail.ch (mail-244106.protonmail.ch [109.224.244.106]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) (No client certificate requested) by smtp.subspace.kernel.org (Postfix) with ESMTPS id DEBDA3B2FC0 for ; Mon, 14 Sep 2026 23:37:56 +0000 (UTC) Authentication-Results: smtp.subspace.kernel.org; arc=none smtp.client-ip=109.224.244.106 ARC-Seal: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789429082; cv=none; b=OpFArh5apqjzH61TqIv98Yq2KT4I8WAVL/WJPcODGGQgSt4kZXjIbpc2Or6j9DPxuC0Y1j/XyD8Hr4rZiO00dl+wCfTeID1ZE53VP0QOzWQ2YB4SW0pFNoQ7Rv9ktsOHqJ3pY58pxaDSx5f79yY7CYQA/0dtRs8k7J2lc8NAUjI= ARC-Message-Signature: i=1; a=rsa-sha256; d=subspace.kernel.org; s=arc-20240116; t=1789429082; c=relaxed/simple; bh=+3l6/hAEENAGMfP0VLMsxBy7amHlxZeguhGYIxUPpQg=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References: MIME-Version; b=PwUlV9nDKwSZrWJUMRLVRjNjTyMOjt3WgRJjPQ1MT1+EupY43wsFLMUwz50qZGn68Vu8HnKbjkqDnlGdjZI2wG2AmGp4YhXPaNQAW8t9FCOmgV2MqOCXfwsEdoMUNYdFQHQnabI3aeMmC3xyDUx244sbH/uhAs+I7syhVLpv5mU= ARC-Authentication-Results: i=1; smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=vasily.cc; spf=pass smtp.mailfrom=vasily.cc; dkim=pass (2048-bit key) header.d=vasily.cc header.i=@vasily.cc header.b=nTa40stb; arc=none smtp.client-ip=109.224.244.106 Authentication-Results: smtp.subspace.kernel.org; dmarc=pass (p=none dis=none) header.from=vasily.cc Authentication-Results: smtp.subspace.kernel.org; spf=pass smtp.mailfrom=vasily.cc Authentication-Results: smtp.subspace.kernel.org; dkim=pass (2048-bit key) header.d=vasily.cc header.i=@vasily.cc header.b="nTa40stb" DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=vasily.cc; s=protonmail2; t=1789429074; x=1789688274; bh=DXm57IH84I+Z/2Zqi7VYiba7Lxjq0R6Hm6M0atmougI=; h=From:To:Cc:Subject:Date:Message-ID:In-Reply-To:References:From:To: Cc:Date:Subject:Reply-To:Feedback-ID:Message-ID:BIMI-Selector; b=nTa40stbC9pZ4JqmpArr/raEydGcvDhQugLHNtBZU3YLTbF1aPG+gswXAFciA4OHd l7D25nbDdnttwDCQtDJHOWldSvFm/2OmVTdOLfERG13qHCOTqS2m2nkjDrEG6OVFSH pTTCBhYRhgSdAMbOKbTg5uGajJx/uXYNju/7HXwglB48uqzHVKEO+MjMCxJglzuBg9 XQx2u6SbdbnKv1B24PpqLtAHODJxn6zkSfbggTHcFIu/B/dmDhlnD4TER34kiZ+Kv4 w7akFmzO+3IIJNkBqyJRnAtthBG1Vejxs1atdTyoMHy0970MvfNJZ2sge9VaEm1TaK bxlj5OAhRSUPA== X-Pm-Submission-Id: 4hkM5X2cdLz1DDX8 From: Vas Zayarskiy To: linux@roeck-us.net, Aleksa Savic , Jack Doan , Jiri Kosina , Benjamin Tissoires Cc: linux-hwmon@vger.kernel.org, linux-input@vger.kernel.org, linux-kernel@vger.kernel.org, linux-doc@vger.kernel.org, Jonathan Corbet , Shuah Khan , Randy Dunlap Subject: [PATCH v3 4/4] hwmon: (aquacomputer_d5next) Add AMPINEL support Date: Tue, 15 Sep 2026 02:37:28 +0300 Message-ID: <20260914233728.22725-5-contact@vasily.cc> X-Mailer: git-send-email 2.54.0 In-Reply-To: <20260914233728.22725-1-contact@vasily.cc> References: <20260914233728.22725-1-contact@vasily.cc> 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 read-only total power, total current, input voltage, hotspot temperature and six channel currents for the Aquacomputer AMPINEL. Decode its 544-byte input report through the existing serial-number, temperature and update path, with power, current and voltage handled in the device-specific switch. Store all readings in hwmon units. Match only the USB telemetry interface, leaving the separate keyboard interface to hid-generic. Validate the telemetry collection and report size before opening the device. Channel labels follow report order; configuration and alarms are not supported. Assisted-by: LLM sparse Signed-off-by: Vas Zayarskiy --- Documentation/hwmon/aquacomputer_d5next.rst | 11 ++ drivers/hwmon/Kconfig | 4 +- drivers/hwmon/aquacomputer_d5next.c | 107 +++++++++++++++++++- 3 files changed, 116 insertions(+), 6 deletions(-) diff --git a/Documentation/hwmon/aquacomputer_d5next.rst b/Documentation/hw= mon/aquacomputer_d5next.rst index 49163f387..faf1c51c1 100644 --- a/Documentation/hwmon/aquacomputer_d5next.rst +++ b/Documentation/hwmon/aquacomputer_d5next.rst @@ -13,6 +13,7 @@ Supported devices: * Aquacomputer Quadro fan controller * Aquacomputer High Flow Next sensor * Aquacomputer Leakshield leak prevention system +* Aquacomputer AMPINEL power monitor * Aquacomputer Aquastream XT watercooling pump * Aquacomputer Aquastream Ultimate watercooling pump * Aquacomputer Poweradjust 3 fan controller @@ -69,6 +70,16 @@ The Aquastream XT pump exposes temperature readings for = the coolant, external se and fan IC. It also exposes pump and fan speeds (in RPM), voltages, as wel= l as pump current. =20 +The AMPINEL exposes total power, total current, input voltage, hotspot +temperature and six channel currents. Channel numbers follow report order; +their mapping to physical connector pins has not been verified. The readin= gs +are received approximately twice per second. Only sensor reads are support= ed; +the driver does not configure protection settings or report alarms. + +For AMPINEL, ``power1_input`` is total power, ``in0_input`` is input volta= ge, +``temp1_input`` is hotspot temperature, ``curr1_input`` is total current, = and +``curr[2-7]_input`` are the six channel currents. + The Aquastream Ultimate pump exposes coolant temp and an external temp sen= sor, along with speed, power, voltage and current of both the pump and optionally con= nected fan. It also exposes pressure and flow speed readings. diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig index 3d8b2c958..06cb246ff 100644 --- a/drivers/hwmon/Kconfig +++ b/drivers/hwmon/Kconfig @@ -257,14 +257,14 @@ config SENSORS_AHT10 will be called aht10. =20 config SENSORS_AQUACOMPUTER_D5NEXT - tristate "Aquacomputer D5 Next, Octo, Quadro, Farbwerk, Farbwerk 360, Hig= h Flow Next" + tristate "Aquacomputer D5 Next and other USB devices" depends on USB_HID select CRC16 help If you say yes here you get support for sensors and fans of the Aquacomputer D5 Next watercooling pump, Octo and Quadro fan controllers, Farbwerk and Farbwerk 360 RGB controllers, High Flow - Next sensor, where available. + Next sensor and AMPINEL power monitor, where available. =20 This driver can also be built as a module. If so, the module will be called aquacomputer_d5next. diff --git a/drivers/hwmon/aquacomputer_d5next.c b/drivers/hwmon/aquacomput= er_d5next.c index b58f5b5c9..e661c4dfa 100644 --- a/drivers/hwmon/aquacomputer_d5next.c +++ b/drivers/hwmon/aquacomputer_d5next.c @@ -2,7 +2,7 @@ /* * hwmon driver for Aquacomputer devices (D5 Next, Farbwerk, Farbwerk 360,= Octo, * Quadro, High Flow Next, Aquaero, Aquastream Ultimate, Leakshield, - * High Flow USB/MPS Flow family) + * High Flow USB/MPS Flow family, AMPINEL) * * Aquacomputer devices send HID reports (with ID 0x01) every second to re= port * sensor values, except for devices that communicate through the @@ -23,6 +23,7 @@ #include #include #include +#include =20 #define USB_VENDOR_ID_AQUACOMPUTER 0x0c70 #define USB_PRODUCT_ID_AQUAERO 0xf001 @@ -33,6 +34,7 @@ #define USB_PRODUCT_ID_OCTO 0xf011 #define USB_PRODUCT_ID_HIGHFLOWNEXT 0xf012 #define USB_PRODUCT_ID_LEAKSHIELD 0xf014 +#define USB_PRODUCT_ID_AMPINEL 0xf015 #define USB_PRODUCT_ID_AQUASTREAMXT 0xf0b6 #define USB_PRODUCT_ID_AQUASTREAMULT 0xf00b #define USB_PRODUCT_ID_POWERADJUST3 0xf0bd @@ -41,7 +43,7 @@ enum kinds { d5next, farbwerk, farbwerk360, octo, quadro, highflownext, aquaero, poweradjust3, aquastreamult, - aquastreamxt, leakshield, highflow + aquastreamxt, leakshield, highflow, ampinel }; =20 static const char *const aqc_device_names[] =3D { @@ -56,7 +58,8 @@ static const char *const aqc_device_names[] =3D { [aquaero] =3D "aquaero", [aquastreamult] =3D "aquastreamultimate", [poweradjust3] =3D "poweradjust3", - [highflow] =3D "highflow" /* Covers MPS Flow devices */ + [highflow] =3D "highflow", /* Covers MPS Flow devices */ + [ampinel] =3D "ampinel" }; =20 #define DRIVER_NAME "aquacomputer_d5next" @@ -111,6 +114,16 @@ static u8 aquaero_secondary_ctrl_report[] =3D { #define AQC_FAN_POWER_OFFSET 0x06 #define AQC_FAN_SPEED_OFFSET 0x08 =20 +/* AMPINEL offsets include the report ID byte. */ +#define AMPINEL_DATA_INTERFACE 1 +#define AMPINEL_STATUS_REPORT_SIZE 544 +#define AMPINEL_NUM_CHANNELS 6 +#define AMPINEL_CHANNEL_CURRENT_START 0x6f +#define AMPINEL_POWER 0xc7 +#define AMPINEL_CURRENT 0xc9 +#define AMPINEL_VOLTAGE 0xcf +#define AMPINEL_HOTSPOT 0xe1 + /* Specs of the Aquaero fan controllers */ #define AQUAERO_SERIAL_START 0x07 #define AQUAERO_FIRMWARE_VERSION 0x0B @@ -516,6 +529,24 @@ static const char *const label_highflow_speeds[] =3D { "Flow speed [dL/h]" }; =20 +static const char *const label_ampinel_temp[] =3D { + "Hotspot" +}; + +static const char *const label_ampinel_power[] =3D { + "Total power" +}; + +static const char *const label_ampinel_voltage[] =3D { + "VCC In" +}; + +static const char *const label_ampinel_current[] =3D { + "Total current", + "Channel 1", "Channel 2", "Channel 3", + "Channel 4", "Channel 5", "Channel 6" +}; + struct aqc_fan_structure_offsets { u8 voltage; u8 curr; @@ -869,6 +900,10 @@ static umode_t aqc_is_visible(const void *data, enum h= wmon_sensor_types type, u3 break; case hwmon_power: switch (priv->kind) { + case ampinel: + if (channel =3D=3D 0) + return 0444; + break; case aquastreamult: /* Special case to support pump and fan power */ if (channel < 2) @@ -889,6 +924,10 @@ static umode_t aqc_is_visible(const void *data, enum h= wmon_sensor_types type, u3 break; case hwmon_curr: switch (priv->kind) { + case ampinel: + if (channel < AMPINEL_NUM_CHANNELS + 1) + return 0444; + break; case aquastreamult: /* Special case to support pump and fan current */ if (channel < 2) @@ -907,6 +946,10 @@ static umode_t aqc_is_visible(const void *data, enum h= wmon_sensor_types type, u3 break; case hwmon_in: switch (priv->kind) { + case ampinel: + if (channel =3D=3D 0) + return 0444; + break; case d5next: /* Special case to support +5V and +12V voltage sensors */ if (channel < priv->num_fans + 2) @@ -1336,12 +1379,17 @@ static int aqc_raw_event(struct hid_device *hdev, s= truct hid_report *report, u8 return 0; =20 priv =3D hid_get_drvdata(hdev); + if (priv->kind =3D=3D ampinel && + (report->type !=3D HID_INPUT_REPORT || size !=3D AMPINEL_STATUS_REPOR= T_SIZE || + data[0] !=3D STATUS_REPORT_ID)) + return 0; =20 /* Info provided with every report */ priv->serial_number[0] =3D get_unaligned_be16(data + priv->serial_number_= start_offset); priv->serial_number[1] =3D get_unaligned_be16(data + priv->serial_number_= start_offset + SERIAL_PART_OFFSET); - priv->firmware_version =3D get_unaligned_be16(data + priv->firmware_versi= on_offset); + if (priv->firmware_version_offset) + priv->firmware_version =3D get_unaligned_be16(data + priv->firmware_vers= ion_offset); =20 /* Physical temperature sensor readings */ for (i =3D 0; i < priv->num_temp_sensors; i++) { @@ -1394,6 +1442,17 @@ static int aqc_raw_event(struct hid_device *hdev, st= ruct hid_report *report, u8 =20 /* Special-case sensor readings */ switch (priv->kind) { + case ampinel: + priv->power_input[0] =3D + min_t(u64, get_unaligned_be16(data + AMPINEL_POWER) * 100000ULL, + LONG_MAX); + priv->current_input[0] =3D get_unaligned_be16(data + AMPINEL_CURRENT) * = 10; + for (i =3D 0; i < AMPINEL_NUM_CHANNELS; i++) + priv->current_input[i + 1] =3D + get_unaligned_be16(data + AMPINEL_CHANNEL_CURRENT_START + + i * AQC_SENSOR_SIZE); + priv->voltage_input[0] =3D get_unaligned_be16(data + AMPINEL_VOLTAGE); + break; case aquaero: /* Read calculated virtual temp sensors */ i =3D priv->num_temp_sensors + priv->num_virtual_temp_sensors; @@ -1519,8 +1578,26 @@ static void aqc_debugfs_init(struct aqc_data *priv) debugfs_create_file("power_cycles", 0444, priv->debugfs, priv, &power_cy= cles_fops); } =20 +static bool aqc_match(struct hid_device *hdev, bool ignore_special_driver) +{ + struct usb_interface *intf; + + if (ignore_special_driver || hdev->quirks & HID_QUIRK_IGNORE_SPECIAL_DRIV= ER) + return false; + + if (hdev->product !=3D USB_PRODUCT_ID_AMPINEL) + return true; + + if (!hid_is_usb(hdev)) + return false; + + intf =3D to_usb_interface(hdev->dev.parent); + return intf->cur_altsetting->desc.bInterfaceNumber =3D=3D AMPINEL_DATA_IN= TERFACE; +} + static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *= id) { + struct hid_report *report; struct aqc_data *priv; int ret; =20 @@ -1537,6 +1614,14 @@ static int aqc_probe(struct hid_device *hdev, const = struct hid_device_id *id) if (ret) return ret; =20 + if (hdev->product =3D=3D USB_PRODUCT_ID_AMPINEL) { + report =3D hdev->report_enum[HID_INPUT_REPORT].report_id_hash[STATUS_REP= ORT_ID]; + if (hdev->maxcollection !=3D 1 || + hdev->collection[0].usage !=3D (HID_UP_MSVENDOR | 1) || + !report || report->size !=3D (AMPINEL_STATUS_REPORT_SIZE - 1) * 8) + return -ENODEV; + } + ret =3D hid_hw_start(hdev, HID_CONNECT_HIDRAW); if (ret) return ret; @@ -1546,6 +1631,15 @@ static int aqc_probe(struct hid_device *hdev, const = struct hid_device_id *id) goto fail_and_stop; =20 switch (hdev->product) { + case USB_PRODUCT_ID_AMPINEL: + priv->kind =3D ampinel; + priv->num_temp_sensors =3D 1; + priv->temp_sensor_start_offset =3D AMPINEL_HOTSPOT; + priv->temp_label =3D label_ampinel_temp; + priv->power_label =3D label_ampinel_power; + priv->voltage_label =3D label_ampinel_voltage; + priv->current_label =3D label_ampinel_current; + break; case USB_PRODUCT_ID_AQUAERO: /* * Aquaero presents itself as three HID devices under the same product I= D: @@ -1795,6 +1889,9 @@ static int aqc_probe(struct hid_device *hdev, const s= truct hid_device_id *id) } =20 switch (priv->kind) { + case ampinel: + priv->serial_number_start_offset =3D AQC_SERIAL_START; + break; case aquaero: priv->serial_number_start_offset =3D AQUAERO_SERIAL_START; priv->firmware_version_offset =3D AQUAERO_FIRMWARE_VERSION; @@ -1890,6 +1987,7 @@ static const struct hid_device_id aqc_table[] =3D { { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_QUADRO) }, { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_HIGHFLOWNEXT)= }, { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_LEAKSHIELD) }, + { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_AMPINEL) }, { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_AQUASTREAMXT)= }, { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_AQUASTREAMULT= ) }, { HID_USB_DEVICE(USB_VENDOR_ID_AQUACOMPUTER, USB_PRODUCT_ID_POWERADJUST3)= }, @@ -1902,6 +2000,7 @@ MODULE_DEVICE_TABLE(hid, aqc_table); static struct hid_driver aqc_driver =3D { .name =3D DRIVER_NAME, .id_table =3D aqc_table, + .match =3D aqc_match, .probe =3D aqc_probe, .remove =3D aqc_remove, .raw_event =3D aqc_raw_event,