[Nut-upsdev] Patch to support Powercool PCRACK-1200VA

Ian Betts ian.betts at ymail.com
Fri Apr 3 14:42:47 BST 2020


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);







More information about the Nut-upsdev mailing list