Skip to content

Commit

Permalink
Merge pull request #1245 from flashydave/issue439
Browse files Browse the repository at this point in the history
  • Loading branch information
jimklimov authored Jan 19, 2022
2 parents 0c5115e + 316089a commit 5998729
Show file tree
Hide file tree
Showing 20 changed files with 154 additions and 34 deletions.
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

0 comments on commit 5998729

Please sign in to comment.