Skip to content

idowell-hid: fix bogus battery.runtime/battery.voltage on GoldenMate LiFePO4 (LLP64 Logical Maximum overflow) - #3555

Open
mkiera wants to merge 2 commits into
networkupstools:masterfrom
mkiera:idowell-goldenmate-logmax-fix
Open

idowell-hid: fix bogus battery.runtime/battery.voltage on GoldenMate LiFePO4 (LLP64 Logical Maximum overflow)#3555
mkiera wants to merge 2 commits into
networkupstools:masterfrom
mkiera:idowell-goldenmate-logmax-fix

Conversation

@mkiera

@mkiera mkiera commented Aug 3, 2026

Copy link
Copy Markdown

Problem

On Windows, a GoldenMate 1500VA/1000W LiFePO4 pack (0x06da:0xffff, the device
added to idowell-hid in PR #3502) reports two nonsense values:

battery.runtime: -16777217
battery.voltage: -167772.2

The same usages read correctly from the device's own ReportID 0x01 Feature
copies (1740 s and 13.2 V), which is what made this traceable.

Root cause

The device declares its data twice: as Feature items in ReportID 0x01 with sane
limits, and again as Input items in ReportID 0x02 — and NUT polls the latter.

In ReportID 0x02 the firmware emits the 4-byte Logical Maximum for
UPS.PowerSummary.RunTimeToEmpty byte-reversed:

09 68              Usage (RunTimeToEmpty)
75 20              Report Size (32)
15 00              Logical Minimum (0)
27 ff ff ff fe     Logical Maximum -> 0xFEFFFFFF     <-- 0xFFFFFFFE was intended
81 83              Input

0xFEFFFFFF is 4278190079, which does not fit a signed 32-bit long.
Windows is LLP64, so long is 32-bit, and the generic LogMax < LogMin
recovery in HIDParse() re-stores it with:

https://github.com/networkupstools/nut/blob/master/drivers/hidparser.c#L423

pParser->Data.LogMax = (long) pParser->Value;

4278190079 wraps to exactly -16777217. From the driver log:

HIDParse: LogMax is less than LogMin. Vendor HID report descriptor may be incorrect;
  interpreting LogMax -16777217 as 4278190079 in ReportID: 0x02

Then, per the HID spec, Logical values persist in global item state. The very
next item, UPS.PowerSummary.Voltage, declares a new Report Size but no new
Logical Maximum, so it silently inherits the broken one:

05 84 09 30        Usage (Voltage)
67 21 d1 f0 00     Unit
55 05              Unit Exponent (5)
75 10              Report Size (16)
81 82              Input          <-- no Logical Maximum of its own

That is how a single firmware typo corrupts two unrelated readings.

This is why CI won't catch it. On LP64 platforms (Linux, the BSDs) long is
64-bit, 4278190079 fits comfortably, and the bug simply does not manifest. It
reproduces only on LLP64 — Windows.

The fix

Adds idowell_fix_report_desc(), following the existing cps-hid.c /
apc-hid.c pattern, replacing the default no-op fix_report_desc in
idowell_subdriver.

Deliberate choices:

  • Gated to 0x06da:0xffff and 0x075d:0x0300 only.
  • Does not invent limits. It uses the values this device itself declares
    for the same usages in ReportID 0x01 — 0x75FFFFFF for RunTimeToEmpty (from
    descriptor bytes 27 ff ff ff 75) and 65535 for Voltage (from
    27 ff ff 00 00).
  • Only corrects demonstrably broken items (LogMax < LogMin), so a firmware
    revision that encodes this correctly is left untouched.
  • Honors disable_fix_report_desc.

Also adds #include "hidparser.h" for FindObject_with_ID_Node()
usbhid-ups.h -> libhid.h -> hidtypes.h provides the types but not the
prototype, so this is needed under -Werror=implicit-function-declaration.

Subdriver version bumped 0.21 -> 0.22.

Testing

Built from this branch with MSYS2/mingw64 on Windows 11 (NUT 2.8.5 install,
usbhid-ups only), tested against the physical device.

Driver log with the fix applied:

Using subdriver: iDowell HID 0.22
Attempting Report Descriptor fix for UPS: Vendor: 06da, Product: ffff
Fixing Report Descriptor: set ReportID 0x02 RunTimeToEmpty LogMax = 1979711487
Fixing Report Descriptor: set ReportID 0x02 Voltage LogMax = 65535
Path: UPS.PowerSummary.RunTimeToEmpty, Type: Input, ReportID: 0x02, Value: 1740
Path: UPS.PowerSummary.Voltage,        Type: Input, ReportID: 0x02, Value: 13.2

The ReportID 0x02 Input values now agree exactly with the ReportID 0x01 Feature
values. Raw report 0x02 decodes cleanly:

02 b2 64 44 07 00 00 28 05
      |  |___________|  |___|
      |  0x744 = 1860 s  0x528 = 1320 -> 13.2 V
      0x64 = 100 %

upsc before / after:

before after
battery.runtime -16777217 1740
battery.voltage -167772.2 13.2

No change in behaviour on LP64 platforms: the guard is LogMax < LogMin, which
is never true there for this descriptor, so the function is a no-op.

Notes

Happy to add a NEWS.adoc entry under the existing usbhid-ups /
idowell-hid bullet — will follow up on this PR shortly.

Two things about this device worth recording for anyone who finds this later:
it exposes only 25 usages, all under UPS.PowerSummary.*, with no
PercentLoad, no Current, and no UPS.PowerConverter / UPS.Flow
collections — so ups.load and the input/output voltage points are genuinely
unavailable rather than unmapped. It also declares a ReportID 0x06 with 32 bytes
of Input as Constant with no usages attached; in testing the device never sends
that report at all.

…LiFePO4

GoldenMate 1000VA/800W LiFePO4 packs (06da:ffff, and the same firmware on
075d:0300) declare their data twice: as Feature items in ReportID 0x01 with
sane limits, and again as Input items in ReportID 0x02.

In ReportID 0x02 the firmware emits the 4-byte Logical Maximum for
UPS.PowerSummary.RunTimeToEmpty byte-reversed, as "27 ff ff ff fe"
(0xFEFFFFFF) where 0xFFFFFFFE was intended:

  09 68              Usage (RunTimeToEmpty)
  75 20              Report Size (32)
  15 00              Logical Minimum (0)
  27 ff ff ff fe     Logical Maximum (0xFEFFFFFF = 4278190079)
  81 83              Input

0xFEFFFFFF does not fit a signed 32-bit long, so on LLP64 platforms (Windows,
where long is 32-bit) the generic "LogMax < LogMin" recovery in HIDParse()
stores it back via "(long) pParser->Value" and it wraps to -16777217. Logical
values persist in HID global state, and the UPS.PowerSummary.Voltage Input
item that follows declares no Logical Maximum of its own, so it inherits the
same broken limit:

  05 84 09 30        Usage (Voltage)
  67 21 d1 f0 00     Unit
  55 05              Unit Exponent (5)
  75 10              Report Size (16)
  81 82              Input          <- no new Logical Maximum

Both readings then come out as nonsense (battery.runtime = -16777217,
battery.voltage = -167772.2) while the ReportID 0x01 Feature copies of the
very same usages read correctly (1740 and 13.2). LP64 platforms are
unaffected, since 4278190079 fits a 64-bit long there.

Add idowell_fix_report_desc() to repair the two ReportID 0x02 items. Rather
than invent limits, use the values this device itself declares for the same
usages in ReportID 0x01: 0x75FFFFFF for RunTimeToEmpty (32-bit) and 65535 for
Voltage (16-bit). Only items whose maximum is demonstrably broken (below the
minimum) are touched, so a firmware revision that encodes this correctly is
left alone, and the user's disable_fix_report_desc toggle is honored.

Signed-off-by: mkiera <itskiera20@gmail.com>
@github-actions

github-actions Bot commented Aug 3, 2026

Copy link
Copy Markdown

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

@mkiera
mkiera force-pushed the idowell-goldenmate-logmax-fix branch from c7f0eb4 to bf2d344 Compare August 3, 2026 14:59
@AppVeyorBot

Copy link
Copy Markdown

@AppVeyorBot

Copy link
Copy Markdown

Build nut 2.8.5.5020-master failed (commit 6795723726 by @mkiera)

@AppVeyorBot

Copy link
Copy Markdown

Build nut 2.8.5.5021-master completed (commit 928954a0e5 by @mkiera)

@AppVeyorBot

Copy link
Copy Markdown

@AppVeyorBot

Copy link
Copy Markdown

Build nut 2.8.5.5022-master completed (commit a41a000055 by @mkiera)

@jimklimov jimklimov added bug USB Phoenixtec (USB 0x06DA/0xFFFF) This USB chip VID/PID is used in many devices, some with different protocols - detection may be hard USB-HID encoding/LogMin/LogMax Issues and solutions (PRs) specifically about incorrect values in bitstream impacts-release-2.8.5 Issues reported against NUT release 2.8.5 (maybe vanilla or with minor packaging tweaks) labels Aug 5, 2026
@jimklimov jimklimov added this to the 2.8.6 milestone Aug 5, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug impacts-release-2.8.5 Issues reported against NUT release 2.8.5 (maybe vanilla or with minor packaging tweaks) Phoenixtec (USB 0x06DA/0xFFFF) This USB chip VID/PID is used in many devices, some with different protocols - detection may be hard 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.

3 participants