Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 5 additions & 38 deletions drivers/cps-hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -485,6 +485,10 @@ static int cps_claim(HIDDevice_t *hd) {
* voltage limits as being more appropriate.
*/





static int cps_fix_report_desc(HIDDevice_t *pDev, HIDDesc_t *pDesc_arg) {
HIDData_t *pData;
int retval = 0;
Expand Down Expand Up @@ -622,29 +626,15 @@ static int cps_fix_report_desc(HIDDevice_t *pDev, HIDDesc_t *pDesc_arg) {
&& input_pData->Size > 1
&& input_pData->Size <= sizeof(long)*8
) {
/* Note: usually values are signed, but
* here we are about compensating for
* poorly encoded maximums, so limit by
* 2^(size)-1, e.g. for "size==16" the
* limit should be "2^16 - 1 = 65535";
* note that in HIDParse() we likely
* set 65535 here in that case. See
* also comments there (hidparser.c)
* discussing signed/unsigned nuances.
*/
/* long sizeMax = (1L << (input_pData->Size - 1)) - 1; */
long sizeMax = (1L << (input_pData->Size)) - 1;
if (input_logmax > sizeMax) {
input_logmax = sizeMax;
}
}

if (output_logmax_assumed
&& output_pData->Size > 1
&& output_pData->Size <= sizeof(long)*8
) {
/* See comment above */
/* long sizeMax = (1L << (output_pData->Size - 1)) - 1; */
long sizeMax = (1L << (output_pData->Size)) - 1;
if (output_logmax > sizeMax) {
output_logmax = sizeMax;
Expand All @@ -670,44 +660,21 @@ static int cps_fix_report_desc(HIDDevice_t *pDev, HIDDesc_t *pDesc_arg) {
}
}

/* Fix for nominal power reporting getting clipped by a too restrictive LogMax. */
if ((pData=FindObject_with_ID_Node(pDesc_arg, 24 /* 0x18 */, USAGE_POW_CONFIG_ACTIVE_POWER))) {
long power_logmax = pData->LogMax;

upsdebugx(4, "Original Report Descriptor: ConfigActivePower "
"LogMin: %ld LogMax: %ld",
pData->LogMin, power_logmax);

if (power_logmax < CPS_NOMINALPWR_LOGMAX) {
/* Set a generous maximum value that will not restrict UPS reporting.
*
* Current findings suggest that the values sent by the UPS are
* accurate, but then get clipped by a too strict LogMax threshold:
* https://github.com/networkupstools/nut/issues/2917#issuecomment-2832243477
*/
pData->LogMax = CPS_NOMINALPWR_LOGMAX;

upsdebugx(3, "Fixing Report Descriptor: "
"set ConfigActivePower LogMax = %ld",
pData->LogMax);

retval = 1;
}
}

if (!retval) {
/* We did not `return 1` above, so... */
upsdebugx(3,
"SKIPPED Report Descriptor fix for UPS: "
"Vendor: %04x, Product: %04x "
"(problematic conditions not matched)",
(unsigned int)vendorID,
(unsigned int)productID);
}

return retval;
}


subdriver_t cps_subdriver = {
CPS_HID_VERSION,
cps_claim,
Expand Down
47 changes: 47 additions & 0 deletions drivers/libhid.c
Original file line number Diff line number Diff line change
Expand Up @@ -151,6 +151,9 @@ reportbuf_t *new_report_buffer(HIDDesc_t *arg_pDesc)
/* the functions in this next group operate on buffered reports, but
operate on individual items, not whole reports. */




/* refresh the report with the given id in the report buffer rbuf. If
the report is not yet in the buffer, or if it is older than "age"
seconds, then the report is freshly read from the USB
Expand Down Expand Up @@ -262,6 +265,49 @@ static int refresh_report_buffer(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDDa
conversion is performed. If age>0, the read operation is buffered
if the item's age is less than "age". On success, return 0 and
store the answer in *value. On failure, return -1 and set errno. */

static long debug_maybe_fix_cps_voltage_raw_value(reportbuf_t *rbuf, HIDData_t *hiddata, long raw_value)
{
usb_ctrl_repindex id;
long reconstructed;
unsigned int b1, b2;

if (!rbuf || !hiddata) {
return raw_value;
}

if (!(hiddata->ReportID == 0x0F || hiddata->ReportID == 0x12)) {
return raw_value;
}

if (hiddata->Offset != 0 || hiddata->Size != 16) {
return raw_value;
}

id = hiddata->ReportID;

if (rbuf->len[id] < 3) {
return raw_value;
}

b1 = (unsigned int)rbuf->data[id][1];
b2 = (unsigned int)rbuf->data[id][2];
reconstructed = (long)(b1 | (b2 << 8));

/* Heuristic for CPS 0764:0601 family:
* some devices expose UPS.Input.Voltage / UPS.Output.Voltage with
* descriptor metadata that leads GetValue() to return only the low byte
* (e.g. 0x0c -> 12) while the two payload bytes actually hold the real
* voltage in 0.1V units (e.g. 0x090c -> 2316 => 231.6V after exponent).
*/
if (raw_value >= 0 && raw_value <= 255 &&
reconstructed >= 1000 && reconstructed <= 3000) {
return reconstructed;
}

return raw_value;
}

static int get_item_buffered(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDData_t *pData, long *Value, time_t age)
{
int id = pData->ReportID;
Expand All @@ -273,6 +319,7 @@ static int get_item_buffered(reportbuf_t *rbuf, hid_dev_handle_t udev, HIDData_t
}

GetValue(rbuf->data[id], pData, Value);
*Value = debug_maybe_fix_cps_voltage_raw_value(rbuf, pData, *Value);

return 0;
}
Expand Down
2 changes: 2 additions & 0 deletions drivers/usbhid-ups.c
Original file line number Diff line number Diff line change
Expand Up @@ -262,6 +262,8 @@ static void ups_status_set(void);
static bool_t hid_ups_walk(walkmode_t mode);
static int reconnect_ups(void);
static int ups_infoval_set(hid_info_t *item, double value);


static int callback(hid_dev_handle_t argudev, HIDDevice_t *arghd,
usb_ctrl_charbuf rdbuf, usb_ctrl_charbufsize rdlen);
#ifdef DEBUG
Expand Down
Loading