Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix #439 Incorrect Output Voltage on CPS*EFPCLCD #1245

Merged
merged 17 commits into from
Jan 19, 2022
Merged
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
1 change: 1 addition & 0 deletions drivers/apc-hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -507,4 +507,5 @@ subdriver_t apc_subdriver = {
apc_format_model,
apc_format_mfr,
apc_format_serial,
fix_report_desc,
};
1 change: 1 addition & 0 deletions drivers/arduino-hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -144,4 +144,5 @@ subdriver_t arduino_subdriver = {
arduino_format_model,
arduino_format_mfr,
arduino_format_serial,
fix_report_desc,
};
1 change: 1 addition & 0 deletions drivers/belkin-hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -641,4 +641,5 @@ subdriver_t belkin_subdriver = {
belkin_format_model,
belkin_format_mfr,
belkin_format_serial,
fix_report_desc,
};
86 changes: 86 additions & 0 deletions drivers/cps-hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,12 @@
/* Cyber Power Systems */
#define CPS_VENDORID 0x0764

/* Values for correcting the HID on some models
* where LogMin and LogMax are set incorrectly in the HID.
*/
#define CPS_VOLTAGE_LOGMIN 0
#define CPS_VOLTAGE_LOGMAX 511 /* Includes safety margin. */

/*! Battery voltage scale factor.
* For some devices, the reported battery voltage is off by factor
* of 1.5 so we need to apply a scale factor to it to get the real
Expand Down Expand Up @@ -262,6 +268,85 @@ static int cps_claim(HIDDevice_t *hd) {
}
}

/* CPS Models like CP900EPFCLCD return a syntactically legal but incorrect
* Report Descriptor whereby the Input High Transfer Max/Min values
* are used for the Output Voltage Usage Item limits.
* Additionally the Input Voltage LogMax is set incorrectly for EU models.
* This corrects them by finding and applying fixed
* voltage limits as being more appropriate.
*/

static HIDData_t *FindReport(HIDDesc_t *pDesc_arg, uint8_t ReportID, HIDNode_t node)
{
size_t i;

for (i = 0; i < pDesc_arg->nitems; i++) {
HIDData_t *pData = &pDesc_arg->item[i];

if (pData->ReportID != ReportID) {
continue;
}

HIDPath_t * pPath = &pData->Path;
uint8_t size = pPath->Size;
if (size == 0 || pPath->Node[size-1] != node) {
continue;
}

return pData;
}

return NULL;
}

static int cps_fix_report_desc(HIDDevice_t *pDev, HIDDesc_t *pDesc_arg) {
HIDData_t *pData;

int vendorID = pDev->VendorID;
int productID = pDev->ProductID;
if (vendorID != CPS_VENDORID || productID != 0x0501) {
return 0;
}

upsdebugx(3, "Attempting Report Descriptor fix for UPS: Vendor: %04x, Product: %04x", vendorID, productID);

/* Apply the fix cautiously by looking for input voltage, high voltage transfer and output voltage report usages.
* If the output voltage log min/max equals high voltage transfer log min/max then the bug is present.
* To fix it Set both the input and output voltages to pre-defined settings.
*/

if ((pData=FindReport(pDesc_arg, 16, (PAGE_POWER_DEVICE<<16)+USAGE_HIGHVOLTAGETRANSFER))) {
long hvt_logmin = pData->LogMin;
long hvt_logmax = pData->LogMax;
upsdebugx(4, "Report Descriptor: hvt input LogMin: %ld LogMax: %ld", hvt_logmin, hvt_logmax);

if ((pData=FindReport(pDesc_arg, 18, (PAGE_POWER_DEVICE<<16)+USAGE_VOLTAGE))) {
long output_logmin = pData->LogMin;
long output_logmax = pData->LogMax;
upsdebugx(4, "Report Descriptor: output LogMin: %ld LogMax: %ld",
output_logmin, output_logmax);

if (hvt_logmin == output_logmin && hvt_logmax == output_logmax) {
pData->LogMin = CPS_VOLTAGE_LOGMIN;
pData->LogMax = CPS_VOLTAGE_LOGMAX;
upsdebugx(3, "Fixing Report Descriptor. Set Output Voltage LogMin = %d, LogMax = %d",
CPS_VOLTAGE_LOGMIN , CPS_VOLTAGE_LOGMAX);
if ((pData=FindReport(pDesc_arg, 15, (PAGE_POWER_DEVICE<<16)+USAGE_VOLTAGE))) {
long input_logmin = pData->LogMin;
long input_logmax = pData->LogMax;
upsdebugx(4, "Report Descriptor: input LogMin: %ld LogMax: %ld",
input_logmin, input_logmax);
upsdebugx(3, "Fixing Report Descriptor. Set Input Voltage LogMin = %d, LogMax = %d",
CPS_VOLTAGE_LOGMIN , CPS_VOLTAGE_LOGMAX);
}

return 1;
}
}
}
return 0;
}

subdriver_t cps_subdriver = {
CPS_HID_VERSION,
cps_claim,
Expand All @@ -270,4 +355,5 @@ subdriver_t cps_subdriver = {
cps_format_model,
cps_format_mfr,
cps_format_serial,
cps_fix_report_desc,
};
1 change: 1 addition & 0 deletions drivers/delta_ups-hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -319,4 +319,5 @@ subdriver_t delta_ups_subdriver = {
delta_ups_format_model,
delta_ups_format_mfr,
delta_ups_format_serial,
fix_report_desc,
};
1 change: 1 addition & 0 deletions drivers/explore-hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -74,4 +74,5 @@ subdriver_t explore_subdriver = {
explore_format_model,
explore_format_mfr,
explore_format_serial,
fix_report_desc,
};
60 changes: 30 additions & 30 deletions drivers/hidparser.c
Original file line number Diff line number Diff line change
Expand Up @@ -375,9 +375,9 @@ static int HIDParse(HIDParser_t *pParser, HIDData_t *pData)
* Get pData characteristics from pData->Path
* Return TRUE if object was found
* -------------------------------------------------------------------------- */
int FindObject(HIDDesc_t *pDesc, HIDData_t *pData)
int FindObject(HIDDesc_t *pDesc_arg, HIDData_t *pData)
{
HIDData_t *pFoundData = FindObject_with_Path(pDesc, &pData->Path, pData->Type);
HIDData_t *pFoundData = FindObject_with_Path(pDesc_arg, &pData->Path, pData->Type);

if (!pFoundData) {
return 0;
Expand All @@ -391,12 +391,12 @@ int FindObject(HIDDesc_t *pDesc, HIDData_t *pData)
* FindObject_with_Path
* Get pData item with given Path and Type. Return NULL if not found.
* -------------------------------------------------------------------------- */
HIDData_t *FindObject_with_Path(HIDDesc_t *pDesc, HIDPath_t *Path, uint8_t Type)
HIDData_t *FindObject_with_Path(HIDDesc_t *pDesc_arg, HIDPath_t *Path, uint8_t Type)
{
size_t i;

for (i = 0; i < pDesc->nitems; i++) {
HIDData_t *pData = &pDesc->item[i];
for (i = 0; i < pDesc_arg->nitems; i++) {
HIDData_t *pData = &pDesc_arg->item[i];

if (pData->Type != Type) {
continue;
Expand All @@ -417,12 +417,12 @@ HIDData_t *FindObject_with_Path(HIDDesc_t *pDesc, HIDPath_t *Path, uint8_t Type)
* Get pData item with given ReportID, Offset, and Type. Return NULL
* if not found.
* -------------------------------------------------------------------------- */
HIDData_t *FindObject_with_ID(HIDDesc_t *pDesc, uint8_t ReportID, uint8_t Offset, uint8_t Type)
HIDData_t *FindObject_with_ID(HIDDesc_t *pDesc_arg, uint8_t ReportID, uint8_t Offset, uint8_t Type)
{
size_t i;

for (i = 0; i < pDesc->nitems; i++) {
HIDData_t *pData = &pDesc->item[i];
for (i = 0; i < pDesc_arg->nitems; i++) {
HIDData_t *pData = &pDesc_arg->item[i];

if (pData->ReportID != ReportID) {
continue;
Expand Down Expand Up @@ -554,10 +554,10 @@ void SetValue(const HIDData_t *pData, unsigned char *Buf, long Value)
HIDDesc_t *Parse_ReportDesc(const usb_ctrl_charbuf ReportDesc, const usb_ctrl_charbufsize n)
{
int ret = 0;
HIDDesc_t *pDesc;
HIDDesc_t *pDesc_var;
HIDParser_t *parser;

pDesc = calloc(1, sizeof(*pDesc));
pDesc_var = calloc(1, sizeof(*pDesc_var));
#if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_UNSIGNED_ZERO_COMPARE) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE) )
# pragma GCC diagnostic push
#endif
Expand All @@ -580,7 +580,7 @@ HIDDesc_t *Parse_ReportDesc(const usb_ctrl_charbuf ReportDesc, const usb_ctrl_ch
#pragma clang diagnostic ignored "-Wtautological-compare"
#pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare"
#endif
if (!pDesc
if (!pDesc_var
|| n < 0 || (uintmax_t)n > SIZE_MAX
) {
return NULL;
Expand All @@ -592,68 +592,68 @@ HIDDesc_t *Parse_ReportDesc(const usb_ctrl_charbuf ReportDesc, const usb_ctrl_ch
# pragma GCC diagnostic pop
#endif

pDesc->item = calloc(MAX_REPORT, sizeof(*pDesc->item));
if (!pDesc->item) {
Free_ReportDesc(pDesc);
pDesc_var->item = calloc(MAX_REPORT, sizeof(*pDesc_var->item));
if (!pDesc_var->item) {
Free_ReportDesc(pDesc_var);
return NULL;
}

parser = calloc(1, sizeof(*parser));
if (!parser) {
Free_ReportDesc(pDesc);
Free_ReportDesc(pDesc_var);
return NULL;
}

parser->ReportDesc = (const unsigned char *)ReportDesc;
parser->ReportDescSize = (const size_t)n;

for (pDesc->nitems = 0; pDesc->nitems < MAX_REPORT; pDesc->nitems += (size_t)ret) {
for (pDesc_var->nitems = 0; pDesc_var->nitems < MAX_REPORT; pDesc_var->nitems += (size_t)ret) {
uint8_t id;
size_t max;

ret = HIDParse(parser, &pDesc->item[pDesc->nitems]);
ret = HIDParse(parser, &pDesc_var->item[pDesc_var->nitems]);
if (ret < 0) {
break;
}

id = pDesc->item[pDesc->nitems].ReportID;
id = pDesc_var->item[pDesc_var->nitems].ReportID;

/* calculate bit range of this item within report */
max = pDesc->item[pDesc->nitems].Offset + pDesc->item[pDesc->nitems].Size;
max = pDesc_var->item[pDesc_var->nitems].Offset + pDesc_var->item[pDesc_var->nitems].Size;

/* convert to bytes */
max = (max + 7) >> 3;

/* update report length */
if (max > pDesc->replen[id]) {
pDesc->replen[id] = max;
if (max > pDesc_var->replen[id]) {
pDesc_var->replen[id] = max;
}
}

/* Sanity check: are there remaining HID objects that can't
* be processed? */
if ((pDesc->nitems == MAX_REPORT) && (parser->Pos < parser->ReportDescSize))
if ((pDesc_var->nitems == MAX_REPORT) && (parser->Pos < parser->ReportDescSize))
upslogx(LOG_ERR, "ERROR in %s: Too many HID objects", __func__);

free(parser);

if (pDesc->nitems == 0) {
Free_ReportDesc(pDesc);
if (pDesc_var->nitems == 0) {
Free_ReportDesc(pDesc_var);
return NULL;
}

pDesc->item = realloc(pDesc->item, pDesc->nitems * sizeof(*pDesc->item));
pDesc_var->item = realloc(pDesc_var->item, pDesc_var->nitems * sizeof(*pDesc_var->item));

return pDesc;
return pDesc_var;
}

/* free a parsed report descriptor, as allocated by Parse_ReportDesc() */
void Free_ReportDesc(HIDDesc_t *pDesc)
void Free_ReportDesc(HIDDesc_t *pDesc_arg)
{
if (!pDesc) {
if (!pDesc_arg) {
return;
}

free(pDesc->item);
free(pDesc);
free(pDesc_arg->item);
free(pDesc_arg);
}
8 changes: 4 additions & 4 deletions drivers/hidparser.h
Original file line number Diff line number Diff line change
Expand Up @@ -52,16 +52,16 @@ HIDDesc_t *Parse_ReportDesc(const usb_ctrl_charbuf ReportDesc, const usb_ctrl_ch
/*
* Free_ReportDesc
* -------------------------------------------------------------------------- */
void Free_ReportDesc(HIDDesc_t *pDesc);
void Free_ReportDesc(HIDDesc_t *pDesc_arg);

/*
* FindObject
* -------------------------------------------------------------------------- */
int FindObject(HIDDesc_t *pDesc, HIDData_t *pData);
int FindObject(HIDDesc_t *pDesc_arg, HIDData_t *pData);

HIDData_t *FindObject_with_Path(HIDDesc_t *pDesc, HIDPath_t *Path, uint8_t Type);
HIDData_t *FindObject_with_Path(HIDDesc_t *pDesc_arg, HIDPath_t *Path, uint8_t Type);

HIDData_t *FindObject_with_ID(HIDDesc_t *pDesc, uint8_t ReportID, uint8_t Offset, uint8_t Type);
HIDData_t *FindObject_with_ID(HIDDesc_t *pDesc_arg, uint8_t ReportID, uint8_t Offset, uint8_t Type);

/*
* GetValue
Expand Down
6 changes: 6 additions & 0 deletions drivers/hidtypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ extern "C" {
#define ATTR_DATA_CST 0x01
#define ATTR_NVOL_VOL 0x80

/* Usage Pages */
#define PAGE_POWER_DEVICE 0x84

/* Usages */
#define USAGE_HIGHVOLTAGETRANSFER 0x54
#define USAGE_VOLTAGE 0x30
/*
* HIDNode_t struct
*
Expand Down
1 change: 1 addition & 0 deletions drivers/idowell-hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -168,4 +168,5 @@ subdriver_t idowell_subdriver = {
idowell_format_model,
idowell_format_mfr,
idowell_format_serial,
fix_report_desc,
};
1 change: 1 addition & 0 deletions drivers/legrand-hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -232,4 +232,5 @@ subdriver_t legrand_subdriver = {
legrand_format_model,
legrand_format_mfr,
legrand_format_serial,
fix_report_desc,
};
1 change: 1 addition & 0 deletions drivers/liebert-hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -148,4 +148,5 @@ subdriver_t liebert_subdriver = {
liebert_format_model,
liebert_format_mfr,
liebert_format_serial,
fix_report_desc,
};
1 change: 1 addition & 0 deletions drivers/mge-hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -1596,4 +1596,5 @@ subdriver_t mge_subdriver = {
mge_format_model,
mge_format_mfr,
mge_format_serial,
fix_report_desc,
};
1 change: 1 addition & 0 deletions drivers/openups-hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -395,4 +395,5 @@ subdriver_t openups_subdriver = {
openups_format_model,
openups_format_mfr,
openups_format_serial,
fix_report_desc,
};
1 change: 1 addition & 0 deletions drivers/powercom-hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -550,4 +550,5 @@ subdriver_t powercom_subdriver = {
powercom_format_model,
powercom_format_mfr,
powercom_format_serial,
fix_report_desc,
};
1 change: 1 addition & 0 deletions drivers/powervar-hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -143,4 +143,5 @@ subdriver_t powervar_subdriver = {
powervar_format_model,
powervar_format_mfr,
powervar_format_serial,
fix_report_desc,
};
1 change: 1 addition & 0 deletions drivers/salicru-hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -253,4 +253,5 @@ subdriver_t salicru_subdriver = {
salicru_format_model,
salicru_format_mfr,
salicru_format_serial,
fix_report_desc,
};
1 change: 1 addition & 0 deletions drivers/tripplite-hid.c
Original file line number Diff line number Diff line change
Expand Up @@ -562,4 +562,5 @@ subdriver_t tripplite_subdriver = {
tripplite_format_model,
tripplite_format_mfr,
tripplite_format_serial,
fix_report_desc,
};
Loading