[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