Skip to content

drivers/cps-hid.c: fix clipped input and battery voltage on CyberPower 0764:0601 - #3575

Open
ktsaou wants to merge 2 commits into
networkupstools:masterfrom
ktsaou:issue-3089-cps-hid-logmax
Open

drivers/cps-hid.c: fix clipped input and battery voltage on CyberPower 0764:0601#3575
ktsaou wants to merge 2 commits into
networkupstools:masterfrom
ktsaou:issue-3089-cps-hid-logmax

Conversation

@ktsaou

@ktsaou ktsaou commented Aug 17, 2026

Copy link
Copy Markdown

Problem

CyberPower 0764:0601 devices (e.g. PR3000ELCDSL / PR3000ELCDSXL, and sibling models sharing the USB ID) transmit correct HID values, but their report descriptor declares Logical Maximums smaller than the values sent. GetValue() (drivers/hidparser.c) then masks with hibit(LogMax) and clamps into [LogMin..LogMax], so usbhid-ups reports:

variable wrong value real value (on the wire, 230 V mains)
input.voltage 70.0 ~225 V
input.voltage.nominal 70 230
battery.voltage 22.4 48.0 (4x12 V)
battery.voltage.nominal 22 48

Covered by issue #3089.

Root cause

Raw captures (usbhid-ups -DDDDDD) and the parsed descriptor limits:

report path wire LogMin/LogMax exponent after mask/clamp reported
0x0F UPS.Input.Voltage 225 0/70 0 70 70.0
0x0E UPS.Input.ConfigVoltage 230 0/70 0 70 70
0x0A UPS.PowerSummary.Voltage 480 0/255 -1 224 -> 22.4 22.4
0x09 UPS.PowerSummary.ConfigVoltage 480 0/255 -1 224 -> 22.4 22
0x12 UPS.Output.Voltage 225 249/261 0 (was already fixed) 225.0

output.voltage was already correct because the existing cps_fix_report_desc() widens it (with CPS_VOLTAGE_LOGMAX 511). Input and battery items were not covered.

Change

All in drivers/cps-hid.c, cps_fix_report_desc() — the subdriver-level fix-up mechanism this device family already uses, gated to vendor 0764 product 0501/0601 and skippable via disable_fix_report_desc:

  • widen the input voltage guard to also fire when the High Voltage Transfer LogMax exceeds the input voltage LogMax (oracle form, mirrors apc-hid.c);
  • widen UPS.Input.ConfigVoltage LogMax to 255 (byte-sized item, mirrors apc-hid.c, resolves input.voltage.nominal);
  • widen UPS.PowerSummary.Voltage / UPS.PowerSummary.ConfigVoltage LogMax to CPS_BATTVOLT_LOGMAX (4096) when declared below it (constant form, mirrors the existing nominal-power fix CPS_NOMINALPWR_LOGMAX, resolves battery.voltage / battery.voltage.nominal).

Raising a LogMax is inert when a device declares zero/undefined physical bounds (all items on this device do), and only widens the mask/clamp window for the affected items on devices with too-strict declared maximums.

Tested on hardware

PR3000ELCDSXL, 0764:0601, firmware CR01901A5P1, 230 V mains.

variable before after
input.voltage 70.0 226.0
input.voltage.nominal 70 230
battery.voltage 22.4 48.0
battery.voltage.nominal 22 48
output.voltage 227.0 226.0 (live value, unchanged behavior)
input.transfer.low/high 202/255 202/255 (unchanged)
driver.version.data CyberPower HID 0.85 CyberPower HID 0.87

New debug lines emitted on the same run:

Fixing Report Descriptor: set Output Voltage LogMin = 0, LogMax = 511
Fixing Report Descriptor: set Input Voltage LogMin = 0, LogMax = 511
Fixing Report Descriptor: set Input ConfigVoltage LogMax = 255
Fixing Report Descriptor: set Battery Voltage LogMax = 4096
Fixing Report Descriptor: set Battery ConfigVoltage LogMax = 4096

With disable_fix_report_desc the fix is fully suppressed (old wrong values return), as intended.

Regression tests

tests/getvaluetest.c gained six cases locking the exact mask/clamp semantics the fix relies on (wire values 225 / 230 / 480 against declared LogMax 70 / 255, and against the widened 511 / 255 / 4096).

Notes on the existing PR #3425

PR #3425 targets the same product ID 0764:0601 but a different firmware variant (that device sends tenths of a volt, 0f e5 08 = 2277, and has no HVT 0x10 item; this device sends whole volts and has HVT). The two changes are independent: #3425's heuristic only fires for reconstructed values in [1000,3000], so it neither fixes nor breaks this device, and this change does not touch that decoding path.

Scope limits (documented, not a regression)

  • The input-side fix-ups are anchored to descriptors that contain the HVT (0x10) item. The HVT-less firmware variant reported in PR drivers: fix CPS HID input/output voltage decoding for 0764:0601 #3425 (same PID 0764:0601, tenths-of-a-volt payloads) is not covered by this change; that PR uses a different decoding mechanism.
  • Items with an active physical-to-logical scaling (Physical Min/Max defined and non-zero) are intentionally skipped: widening LogMax would rescale their already-in-range readings (logical_to_physical() in drivers/libhid.c).

Fixes #3089

…r 0764:0601

PR3000ELCDSL/PR3000ELCDSXL and sibling models sharing USB ID 0764:0601
declare HID Logical Maximums below the values they transmit for
UPS.Input.Voltage/ConfigVoltage (LogMax 70) and
UPS.PowerSummary.Voltage/ConfigVoltage (LogMax 255). GetValue() masks
with hibit(LogMax) and clamps to [LogMin..LogMax], so input.voltage is
reported as 70 and battery.voltage as 22.4 instead of the ~225 / ~48 V
values actually sent on the wire.

Extend cps_fix_report_desc() to widen the under-declared maximums using
the patterns already established for this device family:
 - input voltage: also trigger when the HVT LogMax is greater than the
   input voltage LogMax (oracle sub-form, as used in apc-hid.c), placed
   as a sibling of the output voltage fix under the HVT block
 - input config voltage: set LogMax to 255 when the HVT LogMax is
   greater (byte-sized item; mirrors apc-hid.c)
 - battery voltage / battery config voltage: raise to
   CPS_BATTVOLT_LOGMAX when declared below it (constant sub-form, as
   used for the nominal power fix)

All new fix-ups skip items with an active physical scaling
(logical_to_physical() would rescale their already-in-range values),
stay gated to the existing CPS_VENDORID / 0x0501 / 0x0601 product list,
and remain skippable via disable_fix_report_desc.

Add getvaluetest cases locking the mask/clamp semantics the fix relies
on (wire values 225/230/480 against declared LogMax 70/255 and the
widened 511/255/4096).

Tested on a PR3000ELCDSXL (0764:0601, firmware CR01901A5P1): before the
change input.voltage 70 / battery.voltage 22.4; after 227 / 48.0, with
output.voltage and input transfer limits unchanged. [issue networkupstools#3089]

Signed-off-by: Costa Tsaousis <costa@netdata.cloud>
@github-actions

github-actions Bot commented Aug 17, 2026

Copy link
Copy Markdown

A ZIP file with standard source tarball and another tarball with pre-built docs for commit e13e8c9 is temporarily available: NUT-tarballs-PR-3575.zip.

The NEWS entry for the cps-hid logical-maximum fix references the
CyberPower PR3000ELCDSL and PR3000ELCDSXL models; aspell flags
ELCDSL/ELCDSXL as unknown words and fails the NUT spellcheck and
distcheck stages on the PR CI. Add both model-name fragments to the
accepted words list (alphabetically, next to ELCD).

Signed-off-by: Costa Tsaousis <costa@netdata.cloud>
@jimklimov jimklimov added CyberPower (CPS) USB USB-HID encoding/LogMin/LogMax Issues and solutions (PRs) specifically about incorrect values in bitstream Incorrect or missing readings On some devices driver-reported values are systemically off (e.g. x10, x0.1, const+Value, etc.) impacts-release-2.8.5 Issues reported against NUT release 2.8.5 (maybe vanilla or with minor packaging tweaks) labels Aug 17, 2026
@jimklimov jimklimov added this to the 2.8.6 milestone Aug 17, 2026
@AppVeyorBot

Copy link
Copy Markdown

Build nut 2.8.5.5084-master completed (commit 4b458d5c32 by @ktsaou)

@AppVeyorBot

Copy link
Copy Markdown

Build nut 2.8.5.5085-master completed (commit edea5aa2ca by @ktsaou)

@jimklimov

Copy link
Copy Markdown
Member

Looks great, thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CyberPower (CPS) impacts-release-2.8.5 Issues reported against NUT release 2.8.5 (maybe vanilla or with minor packaging tweaks) Incorrect or missing readings On some devices driver-reported values are systemically off (e.g. x10, x0.1, const+Value, etc.) USB USB-HID encoding/LogMin/LogMax Issues and solutions (PRs) specifically about incorrect values in bitstream

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Incorrect values for battery.voltage, battery.voltage.nominal, input.voltage, input.voltage.nominal on CyberPower PR3000ELCDSL

3 participants