idowell-hid: fix bogus battery.runtime/battery.voltage on GoldenMate LiFePO4 (LLP64 Logical Maximum overflow) - #3555
Open
mkiera wants to merge 2 commits into
Open
Conversation
…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>
|
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
added a commit
to mkiera/nut
that referenced
this pull request
Aug 3, 2026
…kupstools#3555] Signed-off-by: mkiera <itskiera20@gmail.com>
mkiera
force-pushed
the
idowell-goldenmate-logmax-fix
branch
from
August 3, 2026 14:59
c7f0eb4 to
bf2d344
Compare
|
❌ Build nut 2.8.5.5020-master failed (commit 6795723726 by @mkiera) |
|
✅ Build nut 2.8.5.5021-master completed (commit 928954a0e5 by @mkiera)
|
|
✅ Build nut 2.8.5.5021-master completed (commit 928954a0e5 by @mkiera) |
|
✅ Build nut 2.8.5.5022-master completed (commit a41a000055 by @mkiera)
|
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
On Windows, a GoldenMate 1500VA/1000W LiFePO4 pack (
0x06da:0xffff, the deviceadded to
idowell-hidin PR #3502) reports two nonsense values: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.RunTimeToEmptybyte-reversed:0xFEFFFFFFis 4278190079, which does not fit a signed 32-bitlong.Windows is LLP64, so
longis 32-bit, and the genericLogMax < LogMinrecovery in
HIDParse()re-stores it with:https://github.com/networkupstools/nut/blob/master/drivers/hidparser.c#L423
4278190079 wraps to exactly
-16777217. From the driver log: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 newLogical Maximum, so it silently inherits the broken one:
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)
longis64-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 existingcps-hid.c/apc-hid.cpattern, replacing the default no-opfix_report_descinidowell_subdriver.Deliberate choices:
0x06da:0xffffand0x075d:0x0300only.for the same usages in ReportID 0x01 —
0x75FFFFFFfor RunTimeToEmpty (fromdescriptor bytes
27 ff ff ff 75) and65535for Voltage (from27 ff ff 00 00).LogMax < LogMin), so a firmwarerevision that encodes this correctly is left untouched.
disable_fix_report_desc.Also adds
#include "hidparser.h"forFindObject_with_ID_Node()—usbhid-ups.h->libhid.h->hidtypes.hprovides the types but not theprototype, 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-upsonly), tested against the physical device.Driver log with the fix applied:
The ReportID 0x02 Input values now agree exactly with the ReportID 0x01 Feature
values. Raw report 0x02 decodes cleanly:
upscbefore / after:battery.runtime-167772171740battery.voltage-167772.213.2No change in behaviour on LP64 platforms: the guard is
LogMax < LogMin, whichis never true there for this descriptor, so the function is a no-op.
Notes
Happy to add a
NEWS.adocentry under the existingusbhid-ups/idowell-hidbullet — 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 noPercentLoad, noCurrent, and noUPS.PowerConverter/UPS.Flowcollections — so
ups.loadand the input/output voltage points are genuinelyunavailable 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.