Linux Documentation
 help / color / mirror / Atom feed
* [PATCH v5 0/5] hwmon: AMPINEL telemetry support and shared fixes
@ 2026-09-27 19:13 Vas Zayarskiy
  2026-09-27 19:13 ` [PATCH v5 1/5] hwmon: (aquacomputer_d5next) Avoid truncating scaled sensor readings Vas Zayarskiy
                   ` (4 more replies)
  0 siblings, 5 replies; 6+ messages in thread
From: Vas Zayarskiy @ 2026-09-27 19:13 UTC (permalink / raw)
  To: linux, Aleksa Savic, Jack Doan
  Cc: linux-hwmon, linux-kernel, linux-doc, Jonathan Corbet, Shuah Khan,
	Randy Dunlap

This series adds read-only AMPINEL telemetry to aquacomputer_d5next,
along with shared numeric, initial-cache and report-validation fixes.
The separate keyboard interface remains deliberately unsupported, and
there is no HID core or generic-HID change.

Changes since v4:
- In patch 1, read the power cache once with READ_ONCE(), check that
  snapshot for -ENODATA and return the same value. Mark the power-cache
  updates with WRITE_ONCE(). This addresses the check/use race identified
  by Sashiko review; the HID callback does not take the hwmon read mutex.
- In patch 5, use WRITE_ONCE() for AMPINEL's power-cache update as well.
- Patches 2 through 4 are unchanged. The base remains hwmon-next
  9edd5ab2656f (7.3-rc4).

Validation of this revision:
- The hwmon object builds on x86_64 and i386 with W=1 and Sparse.
- All 995 captured reports pass the actual C parser/read replay under
  ASan/UBSan, including simulated 32-bit long clamping and the existing
  shared numeric, expiry and report-validation regression cases.
- A userspace scheduling hook invokes the actual raw-event handler after
  a power-cache read. Valid-to-unavailable, unavailable-to-valid and
  valid-to-different-valid updates preserve the checked snapshot.
  The old double read and two independent READ_ONCE() calls fail this
  check. This models an interleaving, not a live-kernel race reproduction.
- The exact final source was built and installed on Linux 7.2.6 with
  stock hid-generic. All 24 raw USB/hwmon comparisons matched after
  module loading and system activation. Telemetry binds successfully,
  the keyboard remains unbound, and other HID devices retain their
  drivers and input capabilities. No AMPINEL fault or kernel WARN/Oops
  appeared during installation. This revision has not been tested across
  reboot or suspend/resume; the earlier v3 lifecycle results are separate.

The power snapshot does not provide atomic readings across different
sensors. Other Aquacomputer models have synthetic regression coverage,
not hardware validation here. Channel labels follow report order;
alarm reporting and protection configuration remain outside this series.

AI assistance: Codex assisted with implementation, tests and submission
text; Sparse was used for static analysis.

V4: https://lore.kernel.org/linux-hwmon/20260927183723.4078226-1-contact@vasily.cc/
V3: https://lore.kernel.org/linux-hwmon/20260914233728.22725-1-contact@vasily.cc/

Vas Zayarskiy (5):
  hwmon: (aquacomputer_d5next) Avoid truncating scaled sensor readings
  hwmon: (aquacomputer_d5next) Expire the initial sensor cache
  hwmon: (aquacomputer_d5next) Validate incoming status reports
  hwmon: (aquacomputer_d5next) Skip unknown firmware version offsets
  hwmon: (aquacomputer_d5next) Add AMPINEL telemetry support

 Documentation/hwmon/aquacomputer_d5next.rst |  13 +++
 drivers/hwmon/Kconfig                       |   4 +-
 drivers/hwmon/aquacomputer_d5next.c         | 118 +++++++++++++++++---
 3 files changed, 116 insertions(+), 19 deletions(-)


base-commit: 9edd5ab2656f040d726b5388f49ceac10bb2944f

^ permalink raw reply	[flat|nested] 6+ messages in thread

* [PATCH v5 1/5] hwmon: (aquacomputer_d5next) Avoid truncating scaled sensor readings
  2026-09-27 19:13 [PATCH v5 0/5] hwmon: AMPINEL telemetry support and shared fixes Vas Zayarskiy
@ 2026-09-27 19:13 ` Vas Zayarskiy
  2026-09-27 19:13 ` [PATCH v5 2/5] hwmon: (aquacomputer_d5next) Expire the initial sensor cache Vas Zayarskiy
                   ` (3 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Vas Zayarskiy @ 2026-09-27 19:13 UTC (permalink / raw)
  To: linux, Aleksa Savic, Jack Doan
  Cc: linux-hwmon, linux-kernel, linux-doc, Jonathan Corbet, Shuah Khan,
	Randy Dunlap

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.

The power cache is updated by HID reports without the hwmon read mutex.
Read it once with READ_ONCE() and use that snapshot for both the
availability check and the returned value. Mark the corresponding
updates with WRITE_ONCE() so that the shared accesses are explicit.

Widen the shared current cache to u32. The Aquastream XT conversion to
milliamperes can exceed 65535 mA; clamp its negative conversion 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 <contact@vasily.cc>
---
Changes in v5: Snapshot the power cache once for validation and output,
and mark power-cache writes with WRITE_ONCE().

 drivers/hwmon/aquacomputer_d5next.c | 29 ++++++++++++++++++-----------
 1 file changed, 18 insertions(+), 11 deletions(-)

diff --git a/drivers/hwmon/aquacomputer_d5next.c b/drivers/hwmon/aquacomputer_d5next.c
index 1ca70e726..33d292228 100644
--- a/drivers/hwmon/aquacomputer_d5next.c
+++ b/drivers/hwmon/aquacomputer_d5next.c
@@ -19,6 +19,7 @@
 #include <linux/hwmon.h>
 #include <linux/jiffies.h>
 #include <linux/ktime.h>
+#include <linux/limits.h>
 #include <linux/module.h>
 #include <linux/seq_file.h>
 #include <linux/unaligned.h>
@@ -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];
 
 	/* Label values */
 	const char *const *temp_label;
@@ -976,7 +977,7 @@ static int aqc_legacy_read(struct aqc_data *priv)
 
 		/* Calculation derived from linear regression */
 		sensor_value = get_unaligned_le16(priv->buffer + AQUASTREAMXT_PUMP_CURR_OFFSET);
-		priv->current_input[0] = DIV_ROUND_CLOSEST(sensor_value * 176, 100) - 52;
+		priv->current_input[0] = max(DIV_ROUND_CLOSEST(sensor_value * 176, 100) - 52, 0);
 
 		sensor_value = get_unaligned_le16(priv->buffer + AQUASTREAMXT_PUMP_VOLTAGE_OFFSET);
 		priv->voltage_input[0] = DIV_ROUND_CLOSEST(sensor_value * 1000, 61);
@@ -1006,6 +1007,7 @@ static int aqc_legacy_read(struct aqc_data *priv)
 static int aqc_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
 		    int channel, long *val)
 {
+	long power;
 	int ret;
 	struct aqc_data *priv = dev_get_drvdata(dev);
 
@@ -1070,7 +1072,10 @@ static int aqc_read(struct device *dev, enum hwmon_sensor_types type, u32 attr,
 		}
 		break;
 	case hwmon_power:
-		*val = priv->power_input[channel];
+		power = READ_ONCE(priv->power_input[channel]);
+		if (power == -ENODATA)
+			return -ENODATA;
+		*val = power;
 		break;
 	case hwmon_pwm:
 		switch (priv->kind) {
@@ -1368,9 +1373,9 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8
 		priv->speed_input[i] =
 		    get_unaligned_be16(data + priv->fan_sensor_offsets[i] +
 				       priv->fan_structure->speed);
-		priv->power_input[i] =
-		    get_unaligned_be16(data + priv->fan_sensor_offsets[i] +
-				       priv->fan_structure->power) * 10000;
+		WRITE_ONCE(priv->power_input[i],
+			   get_unaligned_be16(data + priv->fan_sensor_offsets[i] +
+					      priv->fan_structure->power) * 10000);
 		priv->voltage_input[i] =
 		    get_unaligned_be16(data + priv->fan_sensor_offsets[i] +
 				       priv->fan_structure->voltage) * 10;
@@ -1410,7 +1415,8 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8
 		priv->speed_input[2] = get_unaligned_be16(data + AQUASTREAMULT_PRESSURE_OFFSET);
 		priv->speed_input[3] = get_unaligned_be16(data + AQUASTREAMULT_FLOW_SENSOR_OFFSET);
 
-		priv->power_input[1] = get_unaligned_be16(data + AQUASTREAMULT_PUMP_POWER) * 10000;
+		WRITE_ONCE(priv->power_input[1],
+			   get_unaligned_be16(data + AQUASTREAMULT_PUMP_POWER) * 10000);
 
 		priv->voltage_input[1] = get_unaligned_be16(data + AQUASTREAMULT_PUMP_VOLTAGE) * 10;
 
@@ -1423,10 +1429,11 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8
 	case highflownext:
 		/* If external temp sensor is not connected, its power reading is also N/A */
 		if (priv->temp_input[1] == -ENODATA)
-			priv->power_input[0] = -ENODATA;
+			WRITE_ONCE(priv->power_input[0], -ENODATA);
 		else
-			priv->power_input[0] =
-			    get_unaligned_be16(data + HIGHFLOWNEXT_POWER) * 1000000;
+			WRITE_ONCE(priv->power_input[0],
+				   min_t(u64, get_unaligned_be16(data + HIGHFLOWNEXT_POWER) *
+					 1000000ULL, LONG_MAX));
 
 		priv->voltage_input[0] = get_unaligned_be16(data + HIGHFLOWNEXT_5V_VOLTAGE) * 10;
 		priv->voltage_input[1] =

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v5 2/5] hwmon: (aquacomputer_d5next) Expire the initial sensor cache
  2026-09-27 19:13 [PATCH v5 0/5] hwmon: AMPINEL telemetry support and shared fixes Vas Zayarskiy
  2026-09-27 19:13 ` [PATCH v5 1/5] hwmon: (aquacomputer_d5next) Avoid truncating scaled sensor readings Vas Zayarskiy
@ 2026-09-27 19:13 ` Vas Zayarskiy
  2026-09-27 19:13 ` [PATCH v5 3/5] hwmon: (aquacomputer_d5next) Validate incoming status reports Vas Zayarskiy
                   ` (2 subsequent siblings)
  4 siblings, 0 replies; 6+ messages in thread
From: Vas Zayarskiy @ 2026-09-27 19:13 UTC (permalink / raw)
  To: linux, Aleksa Savic, Jack Doan
  Cc: linux-hwmon, linux-kernel, linux-doc, Jonathan Corbet, Shuah Khan,
	Randy Dunlap

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 <contact@vasily.cc>
---
 drivers/hwmon/aquacomputer_d5next.c | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/drivers/hwmon/aquacomputer_d5next.c b/drivers/hwmon/aquacomputer_d5next.c
index 33d292228..0bd1886ef 100644
--- a/drivers/hwmon/aquacomputer_d5next.c
+++ b/drivers/hwmon/aquacomputer_d5next.c
@@ -1534,7 +1534,7 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
 	priv->hdev = hdev;
 	hid_set_drvdata(hdev, priv);
 
-	priv->updated = jiffies - STATUS_UPDATE_INTERVAL;
+	priv->updated = jiffies - STATUS_UPDATE_INTERVAL - 1;
 
 	ret = hid_parse(hdev);
 	if (ret)

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v5 3/5] hwmon: (aquacomputer_d5next) Validate incoming status reports
  2026-09-27 19:13 [PATCH v5 0/5] hwmon: AMPINEL telemetry support and shared fixes Vas Zayarskiy
  2026-09-27 19:13 ` [PATCH v5 1/5] hwmon: (aquacomputer_d5next) Avoid truncating scaled sensor readings Vas Zayarskiy
  2026-09-27 19:13 ` [PATCH v5 2/5] hwmon: (aquacomputer_d5next) Expire the initial sensor cache Vas Zayarskiy
@ 2026-09-27 19:13 ` Vas Zayarskiy
  2026-09-27 19:13 ` [PATCH v5 4/5] hwmon: (aquacomputer_d5next) Skip unknown firmware version offsets Vas Zayarskiy
  2026-09-27 19:13 ` [PATCH v5 5/5] hwmon: (aquacomputer_d5next) Add AMPINEL telemetry support Vas Zayarskiy
  4 siblings, 0 replies; 6+ messages in thread
From: Vas Zayarskiy @ 2026-09-27 19:13 UTC (permalink / raw)
  To: linux, Aleksa Savic, Jack Doan
  Cc: linux-hwmon, linux-kernel, linux-doc, Jonathan Corbet, Shuah Khan,
	Randy Dunlap

The raw-event callback runs before HID core pads a short report. Check
that the report is an input report and that its length matches the HID
report descriptor before decoding sensor fields. Otherwise a truncated
report can be read past its received data, and a feature report with the
same ID can be mistaken for sensor data.

Apply these checks to every device using the shared raw-event path and
check the report ID byte before updating the cache. Legacy devices keep
using their separate feature-report read path.

Assisted-by: LLM sparse
Signed-off-by: Vas Zayarskiy <contact@vasily.cc>
---
 drivers/hwmon/aquacomputer_d5next.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/aquacomputer_d5next.c b/drivers/hwmon/aquacomputer_d5next.c
index 0bd1886ef..f80027b27 100644
--- a/drivers/hwmon/aquacomputer_d5next.c
+++ b/drivers/hwmon/aquacomputer_d5next.c
@@ -1334,7 +1334,8 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8
 	int i, j, sensor_value;
 	struct aqc_data *priv;
 
-	if (report->id != STATUS_REPORT_ID)
+	if (report->id != STATUS_REPORT_ID || report->type != HID_INPUT_REPORT ||
+	    size != hid_report_len(report) || data[0] != STATUS_REPORT_ID)
 		return 0;
 
 	priv = hid_get_drvdata(hdev);

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v5 4/5] hwmon: (aquacomputer_d5next) Skip unknown firmware version offsets
  2026-09-27 19:13 [PATCH v5 0/5] hwmon: AMPINEL telemetry support and shared fixes Vas Zayarskiy
                   ` (2 preceding siblings ...)
  2026-09-27 19:13 ` [PATCH v5 3/5] hwmon: (aquacomputer_d5next) Validate incoming status reports Vas Zayarskiy
@ 2026-09-27 19:13 ` Vas Zayarskiy
  2026-09-27 19:13 ` [PATCH v5 5/5] hwmon: (aquacomputer_d5next) Add AMPINEL telemetry support Vas Zayarskiy
  4 siblings, 0 replies; 6+ messages in thread
From: Vas Zayarskiy @ 2026-09-27 19:13 UTC (permalink / raw)
  To: linux, Aleksa Savic, Jack Doan
  Cc: linux-hwmon, linux-kernel, linux-doc, Jonathan Corbet, Shuah Khan,
	Randy Dunlap

Only decode the firmware version when its offset is known, matching the
condition used to expose the firmware_version debugfs file. This lets a
device leave the offset at zero without interpreting the report ID and
following byte as a firmware version.

Assisted-by: LLM sparse
Signed-off-by: Vas Zayarskiy <contact@vasily.cc>
---
 drivers/hwmon/aquacomputer_d5next.c | 3 ++-
 1 file changed, 2 insertions(+), 1 deletion(-)

diff --git a/drivers/hwmon/aquacomputer_d5next.c b/drivers/hwmon/aquacomputer_d5next.c
index f80027b27..13272eb88 100644
--- a/drivers/hwmon/aquacomputer_d5next.c
+++ b/drivers/hwmon/aquacomputer_d5next.c
@@ -1344,7 +1344,8 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8
 	priv->serial_number[0] = get_unaligned_be16(data + priv->serial_number_start_offset);
 	priv->serial_number[1] = get_unaligned_be16(data + priv->serial_number_start_offset +
 						    SERIAL_PART_OFFSET);
-	priv->firmware_version = get_unaligned_be16(data + priv->firmware_version_offset);
+	if (priv->firmware_version_offset)
+		priv->firmware_version = get_unaligned_be16(data + priv->firmware_version_offset);
 
 	/* Physical temperature sensor readings */
 	for (i = 0; i < priv->num_temp_sensors; i++) {

^ permalink raw reply related	[flat|nested] 6+ messages in thread

* [PATCH v5 5/5] hwmon: (aquacomputer_d5next) Add AMPINEL telemetry support
  2026-09-27 19:13 [PATCH v5 0/5] hwmon: AMPINEL telemetry support and shared fixes Vas Zayarskiy
                   ` (3 preceding siblings ...)
  2026-09-27 19:13 ` [PATCH v5 4/5] hwmon: (aquacomputer_d5next) Skip unknown firmware version offsets Vas Zayarskiy
@ 2026-09-27 19:13 ` Vas Zayarskiy
  4 siblings, 0 replies; 6+ messages in thread
From: Vas Zayarskiy @ 2026-09-27 19:13 UTC (permalink / raw)
  To: linux, Aleksa Savic, Jack Doan
  Cc: linux-hwmon, linux-kernel, linux-doc, Jonathan Corbet, Shuah Khan,
	Randy Dunlap

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.

Select the telemetry interface in probe by its vendor collection and
input report layout, following the existing Aquaero and Leakshield
probe filtering. No match callback or HID core change is required.

The separate keyboard interface is not supported. The driver rejects
it in probe, and stock hid-generic yields to this driver's product ID
entry, leaving it unbound. This intentionally limits support to telemetry;
keyboard support can be addressed separately. Channel labels follow
report order; configuration and alarms are not supported.

Assisted-by: LLM sparse
Signed-off-by: Vas Zayarskiy <contact@vasily.cc>
---
Changes in v5: Mark the AMPINEL power-cache write with WRITE_ONCE().

 Documentation/hwmon/aquacomputer_d5next.rst | 13 ++++
 drivers/hwmon/Kconfig                       |  4 +-
 drivers/hwmon/aquacomputer_d5next.c         | 81 ++++++++++++++++++++-
 3 files changed, 93 insertions(+), 5 deletions(-)

diff --git a/Documentation/hwmon/aquacomputer_d5next.rst b/Documentation/hwmon/aquacomputer_d5next.rst
index 49163f387..ad8f85b1c 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,18 @@ 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 well as pump
 current.
 
+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 readings
+are received approximately twice per second. Only sensor reads are supported;
+the driver does not configure protection settings or report alarms.
+The separate keyboard interface is not supported and remains unbound while
+this driver is registered.
+
+For AMPINEL, ``power1_input`` is total power, ``in0_input`` is input voltage,
+``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 sensor, along
 with speed, power, voltage and current of both the pump and optionally connected fan.
 It also exposes pressure and flow speed readings.
diff --git a/drivers/hwmon/Kconfig b/drivers/hwmon/Kconfig
index 18c682e1a..ae90f4894 100644
--- a/drivers/hwmon/Kconfig
+++ b/drivers/hwmon/Kconfig
@@ -257,14 +257,14 @@ config SENSORS_AHT10
 	  will be called aht10.
 
 config SENSORS_AQUACOMPUTER_D5NEXT
-	tristate "Aquacomputer D5 Next, Octo, Quadro, Farbwerk, Farbwerk 360, High 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.
 
 	  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/aquacomputer_d5next.c
index 13272eb88..1fb447be9 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 report
  * sensor values, except for devices that communicate through the
@@ -33,6 +33,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 +42,7 @@
 enum kinds {
 	d5next, farbwerk, farbwerk360, octo, quadro,
 	highflownext, aquaero, poweradjust3, aquastreamult,
-	aquastreamxt, leakshield, highflow
+	aquastreamxt, leakshield, highflow, ampinel
 };
 
 static const char *const aqc_device_names[] = {
@@ -56,7 +57,8 @@ static const char *const aqc_device_names[] = {
 	[aquaero] = "aquaero",
 	[aquastreamult] = "aquastreamultimate",
 	[poweradjust3] = "poweradjust3",
-	[highflow] = "highflow" /* Covers MPS Flow devices */
+	[highflow] = "highflow", /* Covers MPS Flow devices */
+	[ampinel] = "ampinel",
 };
 
 #define DRIVER_NAME			"aquacomputer_d5next"
@@ -111,6 +113,15 @@ static u8 aquaero_secondary_ctrl_report[] = {
 #define AQC_FAN_POWER_OFFSET		0x06
 #define AQC_FAN_SPEED_OFFSET		0x08
 
+/* AMPINEL offsets include the report ID byte. */
+#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 +527,24 @@ static const char *const label_highflow_speeds[] = {
 	"Flow speed [dL/h]"
 };
 
+static const char *const label_ampinel_temp[] = {
+	"Hotspot"
+};
+
+static const char *const label_ampinel_power[] = {
+	"Total power"
+};
+
+static const char *const label_ampinel_voltage[] = {
+	"VCC In"
+};
+
+static const char *const label_ampinel_current[] = {
+	"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 +898,10 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
 		break;
 	case hwmon_power:
 		switch (priv->kind) {
+		case ampinel:
+			if (channel == 0)
+				return 0444;
+			break;
 		case aquastreamult:
 			/* Special case to support pump and fan power */
 			if (channel < 2)
@@ -889,6 +922,10 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_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 +944,10 @@ static umode_t aqc_is_visible(const void *data, enum hwmon_sensor_types type, u3
 		break;
 	case hwmon_in:
 		switch (priv->kind) {
+		case ampinel:
+			if (channel == 0)
+				return 0444;
+			break;
 		case d5next:
 			/* Special case to support +5V and +12V voltage sensors */
 			if (channel < priv->num_fans + 2)
@@ -1398,6 +1439,17 @@ static int aqc_raw_event(struct hid_device *hdev, struct hid_report *report, u8
 
 	/* Special-case sensor readings */
 	switch (priv->kind) {
+	case ampinel:
+		WRITE_ONCE(priv->power_input[0],
+			   min_t(u64, get_unaligned_be16(data + AMPINEL_POWER) * 100000ULL,
+				 LONG_MAX));
+		priv->current_input[0] = get_unaligned_be16(data + AMPINEL_CURRENT) * 10;
+		for (i = 0; i < AMPINEL_NUM_CHANNELS; i++)
+			priv->current_input[i + 1] =
+				get_unaligned_be16(data + AMPINEL_CHANNEL_CURRENT_START +
+						   i * AQC_SENSOR_SIZE);
+		priv->voltage_input[0] = get_unaligned_be16(data + AMPINEL_VOLTAGE);
+		break;
 	case aquaero:
 		/* Read calculated virtual temp sensors */
 		i = priv->num_temp_sensors + priv->num_virtual_temp_sensors;
@@ -1526,6 +1578,7 @@ static void aqc_debugfs_init(struct aqc_data *priv)
 
 static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
 {
+	struct hid_report *report;
 	struct aqc_data *priv;
 	int ret;
 
@@ -1551,6 +1604,24 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
 		goto fail_and_stop;
 
 	switch (hdev->product) {
+	case USB_PRODUCT_ID_AMPINEL:
+		/* The other AMPINEL HID interface is a keyboard. */
+		report = hdev->report_enum[HID_INPUT_REPORT].report_id_hash[STATUS_REPORT_ID];
+		if (hdev->maxcollection != 1 ||
+		    hdev->collection[0].usage != (HID_UP_MSVENDOR | 1) ||
+		    !report || report->size != (AMPINEL_STATUS_REPORT_SIZE - 1) * 8) {
+			ret = -ENODEV;
+			goto fail_and_close;
+		}
+
+		priv->kind = ampinel;
+		priv->num_temp_sensors = 1;
+		priv->temp_sensor_start_offset = AMPINEL_HOTSPOT;
+		priv->temp_label = label_ampinel_temp;
+		priv->power_label = label_ampinel_power;
+		priv->voltage_label = label_ampinel_voltage;
+		priv->current_label = label_ampinel_current;
+		break;
 	case USB_PRODUCT_ID_AQUAERO:
 		/*
 		 * Aquaero presents itself as three HID devices under the same product ID:
@@ -1800,6 +1871,9 @@ static int aqc_probe(struct hid_device *hdev, const struct hid_device_id *id)
 	}
 
 	switch (priv->kind) {
+	case ampinel:
+		priv->serial_number_start_offset = AQC_SERIAL_START;
+		break;
 	case aquaero:
 		priv->serial_number_start_offset = AQUAERO_SERIAL_START;
 		priv->firmware_version_offset = AQUAERO_FIRMWARE_VERSION;
@@ -1895,6 +1969,7 @@ static const struct hid_device_id aqc_table[] = {
 	{ 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) },

^ permalink raw reply related	[flat|nested] 6+ messages in thread

end of thread, other threads:[~2026-09-27 19:13 UTC | newest]

Thread overview: 6+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2026-09-27 19:13 [PATCH v5 0/5] hwmon: AMPINEL telemetry support and shared fixes Vas Zayarskiy
2026-09-27 19:13 ` [PATCH v5 1/5] hwmon: (aquacomputer_d5next) Avoid truncating scaled sensor readings Vas Zayarskiy
2026-09-27 19:13 ` [PATCH v5 2/5] hwmon: (aquacomputer_d5next) Expire the initial sensor cache Vas Zayarskiy
2026-09-27 19:13 ` [PATCH v5 3/5] hwmon: (aquacomputer_d5next) Validate incoming status reports Vas Zayarskiy
2026-09-27 19:13 ` [PATCH v5 4/5] hwmon: (aquacomputer_d5next) Skip unknown firmware version offsets Vas Zayarskiy
2026-09-27 19:13 ` [PATCH v5 5/5] hwmon: (aquacomputer_d5next) Add AMPINEL telemetry support Vas Zayarskiy

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox