Hi Folks, This is my first post on nut-upsdev. I would like to share a small patch to enable support for the Powercool PCRACK 1200VA ups. I found that the UPS uses megatec/krauler protocol but is sensitive to the USB buffer length passed to it in requests via usb_get_string(), and usb_get_string_simple(). If the buflen is greater than 102 then the ups will reply to requests but does not behave correctly. In this case the responses to "Q1" commands contain constant stale data that is never updated. This patch adds a parameter to the nutdrv_qx driver that allows the value of buflen to be set explicitly from /etc/nut/ups.conf It does not alter the actual buffer size, only the value passed to usb_get_xxx. It is defensively programmed to ensure that the requested size cannot be larger than the actual buffer. It seems (at least on my debian 10 system) that the implementation of usb_get_string_simple() anyway overrides the passed buflen value and codes it as 255. The consequence is that to support this UPS the "langid_fix" parameter is also needed, to force avoidance of calls to usb_get_string_simple() The UPS is working with this patch plus following entry in ups.conf [POWERCOOL] driver = nutdrv_qx port = auto protocol=megatec langid_fix=0x409 buflen_fix=102 desc = "POWERCOOL" Patch is below, I hope it is of assistance. Regards, Ian diff --git a/drivers/nutdrv_qx.c b/drivers/nutdrv_qx.c index bb526608..75bb97f8 100644 --- a/drivers/nutdrv_qx.c +++ b/drivers/nutdrv_qx.c @@ -422,6 +422,7 @@ static USBDevice_t usbdevice; static USBDeviceMatcher_t *reopen_matcher = NULL; static USBDeviceMatcher_t *regex_matcher = NULL; static int langid_fix = -1; +static int buflen_fix = -1; static int (*subdriver_command)(const char *cmd, char *buf, size_t buflen) = NULL; @@ -702,8 +703,15 @@ static int krauler_command(const char *cmd, char *buf, size_t buflen) { NULL } }; - int i; + /* check for buflen fix */ + int blen = buflen; + if (buflen_fix != -1) { + /* set reported buflen explicitly */ + blen = buflen_fix; + } + int i; +dif upsdebugx(3, "send: %.*s", (int)strcspn(cmd, "\r"), cmd); for (i = 0; command[i].str; i++) { @@ -720,9 +728,9 @@ static int krauler_command(const char *cmd, char *buf, size_t buflen) if (langid_fix != -1) { /* Apply langid_fix value */ - ret = usb_get_string(udev, command[i].index, langid_fix, buf, buflen); + ret = usb_get_string(udev, command[i].index, langid_fix, buf, blen); } else { - ret = usb_get_string_simple(udev, command[i].index, buf, buflen); + ret = usb_get_string_simple(udev, command[i].index, buf, blen); } if (ret <= 0) { @@ -1075,6 +1083,7 @@ static qx_usb_device_id_t qx_usb_id[] = { { USB_DEVICE(0x0001, 0x0000), "MEC", "MEC0003", &fabula_subdriver }, /* Fideltronik/MEC LUPUS 500 USB */ { USB_DEVICE(0x0001, 0x0000), "ATCL FOR UPS", "ATCL FOR UPS", &fuji_subdriver }, /* Fuji UPSes */ { USB_DEVICE(0x0001, 0x0000), NULL, NULL, &krauler_subdriver }, /* Krauler UP-M500VA */ + { USB_DEVICE(0x0001, 0x0000), NULL, "MEC0003", &krauler_subdriver }, /* Powercool PCRACK-1200VA */ /* End of list */ { -1, -1, NULL, NULL, NULL } }; @@ -1651,6 +1660,9 @@ void upsdrv_makevartable(void) nut_usb_addvars(); addvar(VAR_VALUE, "langid_fix", "Apply the language ID workaround to the krauler subdriver (0x409 or 0x4095)"); + + addvar(VAR_VALUE, "buflen_fix", "Apply the buflen workaround to the krauler subdriver"); + #endif /* QX_USB */ #ifdef QX_SERIAL @@ -1822,7 +1834,8 @@ void upsdrv_initups(void) getval("product") || getval("serial") || getval("bus") || - getval("langid_fix") + getval("langid_fix") || + getval("buflen_fix") ) { /* USB */ is_usb = 1; @@ -1941,6 +1954,22 @@ void upsdrv_initups(void) regex_array[4] = getval("serial"); regex_array[5] = getval("bus"); + /* check for buflen workaround */ + int blen; + if(getval("buflen_fix")) { + if(sscanf(getval("buflen_fix"),"%d",&blen) != 1) { + upslogx(LOG_NOTICE, "Error enabling buflen workaround"); + } else { + if (blen >SMALLBUF) { + upslogx(LOG_NOTICE, + "Error enabling buflen workaround, buflen must be <= %d", + SMALLBUF); + } else { + buflen_fix = blen; + } + } + } + /* Check for language ID workaround (#1) */ if (getval("langid_fix")) { /* Skip "0x" prefix and set back to hexadecimal */ @@ -2024,7 +2053,13 @@ void upsdrv_initups(void) * The language IDs are 16 bit numbers, and they start at the third byte in the descriptor. * See USB 2.0 specification, section 9.6.7, for more information on this. * This should allow automatic application of the workaround */ - ret = usb_get_string(udev, 0, 0, tbuf, sizeof(tbuf)); + + /* check for buflen fix */ + blen = sizeof(tbuf); + if ((buflen_fix != -1) && (buflen_fix < (int)sizeof(tbuf))) { + blen = buflen_fix; + } + ret = usb_get_string(udev, 0, 0, tbuf, blen); if (ret >= 4) { langid = tbuf[2] | (tbuf[3] << 8); upsdebugx(1, "First supported language ID: 0x%x (please report to the NUT maintainer!)", langid);