apcupsd-ups: ignore generated files
[networkupstools/kirr.git] / drivers / nutdrv_qx.c
blobefde86e0bafcd00effade8c9489e6a1fa9af4209
1 /* nutdrv_qx.c - Driver for USB and serial UPS units with Q* protocols
3 * Copyright (C)
4 * 2013 Daniele Pezzini <hyouko@gmail.com>
5 * Based on:
6 * usbhid-ups.c - Copyright (C)
7 * 2003-2012 Arnaud Quette <arnaud.quette@gmail.com>
8 * 2005 John Stamp <kinsayder@hotmail.com>
9 * 2005-2006 Peter Selinger <selinger@users.sourceforge.net>
10 * 2007-2009 Arjen de Korte <adkorte-guest@alioth.debian.org>
11 * blazer.c - Copyright (C)
12 * 2008-2009 Arjen de Korte <adkorte-guest@alioth.debian.org>
13 * 2012 Arnaud Quette <ArnaudQuette@Eaton.com>
14 * blazer_ser.c - Copyright (C)
15 * 2008 Arjen de Korte <adkorte-guest@alioth.debian.org>
16 * blazer_usb.c - Copyright (C)
17 * 2003-2009 Arjen de Korte <adkorte-guest@alioth.debian.org>
18 * 2011-2012 Arnaud Quette <arnaud.quette@free.fr>
20 * This program is free software; you can redistribute it and/or modify
21 * it under the terms of the GNU General Public License as published by
22 * the Free Software Foundation; either version 2 of the License, or
23 * (at your option) any later version.
25 * This program is distributed in the hope that it will be useful,
26 * but WITHOUT ANY WARRANTY; without even the implied warranty of
27 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
28 * GNU General Public License for more details.
30 * You should have received a copy of the GNU General Public License
31 * along with this program; if not, write to the Free Software
32 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
36 #define DRIVER_VERSION "0.01"
38 #include "main.h"
40 #include <math.h>
42 /* note: QX_USB/QX_SERIAL set through Makefile */
43 #ifdef QX_USB
44 #include "libusb.h"
45 #include "usb-common.h"
47 #ifdef QX_SERIAL
48 #define DRIVER_NAME "Generic Q* USB/Serial driver"
49 #else
50 #define DRIVER_NAME "Generic Q* USB driver"
51 #endif /* QX_SERIAL */
52 #else
53 #define DRIVER_NAME "Generic Q* Serial driver"
54 #endif /* QX_USB */
56 #ifdef QX_SERIAL
57 #include "serial.h"
58 #define SER_WAIT_SEC 1 /* 3 seconds for Best UPS */
59 #endif /* QX_SERIAL */
61 #include "nutdrv_qx.h"
63 /* == Subdrivers == */
64 /* Include all known subdrivers */
65 #include "nutdrv_qx_mecer.h"
66 #include "nutdrv_qx_megatec.h"
67 #include "nutdrv_qx_megatec-old.h"
68 #include "nutdrv_qx_mustek.h"
69 #include "nutdrv_qx_voltronic.h"
70 #include "nutdrv_qx_zinto.h"
72 /* Master list of avaiable subdrivers */
73 static subdriver_t *subdriver_list[] = {
74 &voltronic_subdriver,
75 &mustek_subdriver,
76 &megatec_old_subdriver,
77 &megatec_subdriver,
78 &mecer_subdriver,
79 &zinto_subdriver,
80 NULL
84 /* == Driver description structure == */
85 upsdrv_info_t upsdrv_info = {
86 DRIVER_NAME,
87 DRIVER_VERSION,
88 "Daniele Pezzini <hyouko@gmail.com>" \
89 "Arnaud Quette <arnaud.quette@gmail.com>" \
90 "John Stamp <kinsayder@hotmail.com>" \
91 "Peter Selinger <selinger@users.sourceforge.net>" \
92 "Arjen de Korte <adkorte-guest@alioth.debian.org>",
93 DRV_BETA,
94 #ifdef QX_USB
95 { &comm_upsdrv_info, NULL }
96 #else
97 { NULL }
98 #endif /* QX_USB */
102 /* == Data walk modes == */
103 typedef enum {
104 QX_WALKMODE_INIT = 0,
105 QX_WALKMODE_QUICK_UPDATE,
106 QX_WALKMODE_FULL_UPDATE
107 } walkmode_t;
110 /* == Global vars == */
111 /* Pointer to the active subdriver object (changed in subdriver_matcher() function) */
112 static subdriver_t *subdriver = NULL;
114 static int pollfreq = DEFAULT_POLLFREQ;
115 static int ups_status = 0;
116 static bool_t data_has_changed = FALSE; /* for SEMI_STATIC data polling */
118 static time_t lastpoll; /* Timestamp the last polling */
120 #if defined(QX_USB) && defined(QX_SERIAL)
121 static int is_usb = 0; /* Whether the device is connected through USB (1) or serial (0) */
122 #endif /* QX_USB && QX_SERIAL */
124 static struct {
125 char command[SMALLBUF]; /* Command sent to the UPS to get answer/to execute an instant command */
126 char answer[SMALLBUF]; /* Answer from the UPS, filled at runtime */
127 } previous_item = { "", "" }; /* Hold the values of the item processed just before the actual one */
130 /* == Support functions == */
131 static int subdriver_matcher(void);
132 static int qx_command(const char *cmd, char *buf, size_t buflen);
133 static int qx_process_answer(item_t *item, const int len);
134 static bool_t qx_ups_walk(walkmode_t mode);
135 static void ups_status_set(void);
136 static void ups_alarm_set(void);
137 static void qx_set_var(item_t *item);
140 /* == Struct & data for status processing == */
141 typedef struct {
142 const char *status_str; /* UPS status string */
143 const int status_mask; /* UPS status mask */
144 } status_lkp_t;
146 static status_lkp_t status_info[] = {
147 /* Map status strings to bit masks */
148 { "OL", STATUS(OL) },
149 { "LB", STATUS(LB) },
150 { "RB", STATUS(RB) },
151 { "CHRG", STATUS(CHRG) },
152 { "DISCHRG", STATUS(DISCHRG) },
153 { "BYPASS", STATUS(BYPASS) },
154 { "CAL", STATUS(CAL) },
155 { "OFF", STATUS(OFF) },
156 { "OVER", STATUS(OVER) },
157 { "TRIM", STATUS(TRIM) },
158 { "BOOST", STATUS(BOOST) },
159 { "FSD", STATUS(FSD) },
160 { NULL, 0 },
164 /* == battery.{charge,runtime} guesstimation == */
165 /* Support functions */
166 static int qx_battery(void);
167 static int qx_load(void);
168 static void qx_initbattery(void);
170 /* Battery data */
171 static struct {
172 double packs; /* Battery voltage multiplier */
173 struct {
174 double act; /* Actual runtime on battery */
175 double nom; /* Nominal runtime on battery (full load) */
176 double est; /* Estimated runtime remaining (full load) */
177 double exp; /* Load exponent */
178 } runt;
179 struct {
180 double act; /* Actual battery voltage */
181 double high; /* Battery float voltage */
182 double nom; /* Nominal battery voltage */
183 double low; /* Battery low voltage */
184 } volt;
185 struct {
186 double act; /* Actual battery charge */
187 long time; /* Recharge time from empty to full */
188 } chrg;
189 } batt = { 1, { -1, -1, 0, 0 }, { -1, -1, -1, -1 }, { -1, 43200 } };
191 /* Load data */
192 static struct {
193 double act; /* Actual load (reported by the UPS) */
194 double low; /* Idle load */
195 double eff; /* Effective load */
196 } load = { 0, 0.1, 1 };
198 static time_t battery_lastpoll = 0;
200 /* Fill batt.volt.act and guesstimate the battery charge if it isn't already available. */
201 static int qx_battery(void)
203 const char *val = dstate_getinfo("battery.voltage");
205 if (!val) {
206 upsdebugx(2, "%s: unable to get battery.voltage", __func__);
207 return -1;
210 batt.volt.act = batt.packs * strtod(val, NULL);
212 if (batt.chrg.act == -1 && batt.volt.low > 0 && batt.volt.high > batt.volt.low) {
214 batt.chrg.act = 100 * (batt.volt.act - batt.volt.low) / (batt.volt.high - batt.volt.low);
216 if (batt.chrg.act < 0) {
217 batt.chrg.act = 0;
220 if (batt.chrg.act > 100) {
221 batt.chrg.act = 100;
224 dstate_setinfo("battery.charge", "%.0f", batt.chrg.act);
228 return 0;
231 /* Load for battery.{charge,runtime} from runtimecal */
232 static int qx_load(void)
234 const char *val = dstate_getinfo("ups.load");
236 if (!val) {
237 upsdebugx(2, "%s: unable to get ups.load", __func__);
238 return -1;
241 load.act = strtod(val, NULL);
243 load.eff = pow(load.act / 100, batt.runt.exp);
245 if (load.eff < load.low) {
246 load.eff = load.low;
249 return 0;
252 /* Guesstimation: init */
253 static void qx_initbattery(void)
255 if (!dstate_getinfo("battery.charge") || !dstate_getinfo("battery.runtime")) {
257 const char *val;
259 val = dstate_getinfo("battery.voltage.high");
260 if (val) {
261 batt.volt.high = strtod(val, NULL);
264 val = dstate_getinfo("battery.voltage.low");
265 if (val) {
266 batt.volt.low = strtod(val, NULL);
269 val = dstate_getinfo("battery.voltage.nominal");
270 if (val) {
271 batt.volt.nom = strtod(val, NULL);
274 /* If no values are available for both battery.voltage.{low,high} either from the UPS or provided by the user in ups.conf, try to guesstimate them, but announce it! */
275 if (batt.volt.nom != -1 && (batt.volt.low == -1 || batt.volt.high == -1)) {
277 upslogx(LOG_INFO, "No values for battery high/low voltages");
279 /* Basic formula, which should cover most cases */
280 batt.volt.low = 104 * batt.volt.nom / 120;
281 batt.volt.high = 130 * batt.volt.nom / 120;
283 /* Publish these data too */
284 dstate_setinfo("battery.voltage.low", "%.2f", batt.volt.low);
285 dstate_setinfo("battery.voltage.high", "%.2f", batt.volt.high);
287 upslogx(LOG_INFO, "Using 'guesstimation' (low: %f, high: %f)!", batt.volt.low, batt.volt.high);
291 val = dstate_getinfo("battery.packs");
292 if (val && (strspn(val, "0123456789 .") == strlen(val))) {
293 batt.packs = strtod(val, NULL);
294 } else {
296 /* qx_battery -> batt.volt.act */
297 if (!qx_battery() && batt.volt.nom != -1) {
299 const double packs[] = { 120, 100, 80, 60, 48, 36, 30, 24, 18, 12, 8, 6, 4, 3, 2, 1, 0.5, -1 };
300 int i;
302 /* The battery voltage will quickly return to at least the nominal value after discharging them.
303 * For overlapping battery.voltage.low/high ranges therefor choose the one with the highest multiplier. */
304 for (i = 0; packs[i] > 0; i++) {
306 if (packs[i] * batt.volt.act > 1.2 * batt.volt.nom) {
307 continue;
310 if (packs[i] * batt.volt.act < 0.8 * batt.volt.nom) {
311 upslogx(LOG_INFO, "Can't autodetect number of battery packs [%.0f/%.2f]", batt.volt.nom, batt.volt.act);
312 break;
315 batt.packs = packs[i];
316 break;
320 } else {
321 upslogx(LOG_INFO, "Can't autodetect number of battery packs [%.0f/%.2f]", batt.volt.nom, batt.volt.act);
326 /* Update batt.{chrg,volt}.act */
327 qx_battery();
329 val = getval("runtimecal");
330 if (val) {
332 double rh, lh, rl, ll;
334 time(&battery_lastpoll);
336 if (sscanf(val, "%lf,%lf,%lf,%lf", &rh, &lh, &rl, &ll) < 4) {
337 fatalx(EXIT_FAILURE, "Insufficient parameters for runtimecal");
340 if ((rl < rh) || (rh <= 0)) {
341 fatalx(EXIT_FAILURE, "Parameter out of range (runtime)");
344 if ((lh > 100) || (ll > lh) || (ll <= 0)) {
345 fatalx(EXIT_FAILURE, "Parameter out of range (load)");
348 batt.runt.exp = log(rl / rh) / log(lh / ll);
349 upsdebugx(2, "%s: battery runtime exponent: %.3f", __func__, batt.runt.exp);
351 batt.runt.nom = rh * pow(lh / 100, batt.runt.exp);
352 upsdebugx(2, "%s: battery runtime nominal: %.1f", __func__, batt.runt.nom);
354 } else {
356 upslogx(LOG_INFO, "Battery runtime will not be calculated (runtimecal not set)");
357 return;
361 val = dstate_getinfo("battery.charge");
362 if (!val && batt.volt.nom != -1) {
363 batt.volt.low = batt.volt.nom;
364 batt.volt.high = 1.15 * batt.volt.nom;
366 if (qx_battery())
367 fatalx(EXIT_FAILURE, "Initial battery charge undetermined");
369 val = dstate_getinfo("battery.charge");
372 if (val) {
373 batt.runt.est = batt.runt.nom * strtod(val, NULL) / 100;
374 upsdebugx(2, "%s: battery runtime estimate: %.1f", __func__, batt.runt.est);
375 } else {
376 fatalx(EXIT_FAILURE, "Initial battery charge undetermined");
379 val = getval("chargetime");
380 if (val) {
381 batt.chrg.time = strtol(val, NULL, 10);
383 if (batt.chrg.time <= 0) {
384 fatalx(EXIT_FAILURE, "Charge time out of range [1..s]");
387 upsdebugx(2, "%s: battery charge time: %ld", __func__, batt.chrg.time);
388 } else {
389 upslogx(LOG_INFO, "No charge time specified, using built in default [%ld seconds]", batt.chrg.time);
392 val = getval("idleload");
393 if (val) {
394 load.low = strtod(val, NULL) / 100;
396 if ((load.low <= 0) || (load.low > 1)) {
397 fatalx(EXIT_FAILURE, "Idle load out of range [0..100]");
400 upsdebugx(2, "%s: minimum load used (idle): %.3f", __func__, load.low);
401 } else {
402 upslogx(LOG_INFO, "No idle load specified, using built in default [%.1f %%]", 100 * load.low);
408 /* == USB communication subdrivers == */
409 #if defined(QX_USB) && !defined(TESTING)
410 static usb_communication_subdriver_t *usb = &usb_subdriver;
411 static usb_dev_handle *udev = NULL;
412 static USBDevice_t usbdevice;
413 static USBDeviceMatcher_t *reopen_matcher = NULL;
414 static USBDeviceMatcher_t *regex_matcher = NULL;
415 static int langid_fix = -1;
417 static int (*subdriver_command)(const char *cmd, char *buf, size_t buflen) = NULL;
419 /* Cypress communication subdriver */
420 static int cypress_command(const char *cmd, char *buf, size_t buflen)
422 char tmp[SMALLBUF];
423 int ret;
424 size_t i;
426 /* Send command */
427 memset(tmp, 0, sizeof(tmp));
428 snprintf(tmp, sizeof(tmp), "%s", cmd);
430 for (i = 0; i < strlen(tmp); i += ret) {
432 /* Write data in 8-byte chunks */
433 /* ret = usb->set_report(udev, 0, (unsigned char *)&tmp[i], 8); */
434 ret = usb_control_msg(udev, USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE, 0x09, 0x200, 0, &tmp[i], 8, 5000);
436 if (ret <= 0) {
437 upsdebugx(3, "send: %s", ret ? usb_strerror() : "timeout");
438 return ret;
442 upsdebugx(3, "send: %.*s", (int)strcspn(tmp, "\r"), tmp);
444 /* Read reply */
445 memset(buf, 0, buflen);
447 for (i = 0; (i <= buflen-8) && (strchr(buf, '\r') == NULL); i += ret) {
449 /* Read data in 8-byte chunks */
450 /* ret = usb->get_interrupt(udev, (unsigned char *)&buf[i], 8, 1000); */
451 ret = usb_interrupt_read(udev, 0x81, &buf[i], 8, 1000);
453 /* Any errors here mean that we are unable to read a reply (which will happen after successfully writing a command to the UPS) */
454 if (ret <= 0) {
455 upsdebugx(3, "read: %s", ret ? usb_strerror() : "timeout");
456 return ret;
460 upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf);
461 return i;
464 /* Phoenix communication subdriver */
465 static int phoenix_command(const char *cmd, char *buf, size_t buflen)
467 char tmp[SMALLBUF];
468 int ret;
469 size_t i;
471 for (i = 0; i < 8; i++) {
473 /* Read data in 8-byte chunks */
474 /* ret = usb->get_interrupt(udev, (unsigned char *)tmp, 8, 1000); */
475 ret = usb_interrupt_read(udev, 0x81, tmp, 8, 1000);
477 /* This USB to serial implementation is crappy.
478 * In order to read correct replies we need to flush the output buffers of the converter until we get no more data (ie, it times out). */
479 switch (ret)
481 case -EPIPE: /* Broken pipe */
482 usb_clear_halt(udev, 0x81);
483 case -ETIMEDOUT: /* Connection timed out */
484 break;
487 if (ret < 0) {
488 upsdebugx(3, "flush: %s", usb_strerror());
489 break;
492 upsdebug_hex(4, "dump", tmp, ret);
495 /* Send command */
496 memset(tmp, 0, sizeof(tmp));
497 snprintf(tmp, sizeof(tmp), "%s", cmd);
499 for (i = 0; i < strlen(tmp); i += ret) {
501 /* Write data in 8-byte chunks */
502 /* ret = usb->set_report(udev, 0, (unsigned char *)&tmp[i], 8); */
503 ret = usb_control_msg(udev, USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE, 0x09, 0x200, 0, &tmp[i], 8, 1000);
505 if (ret <= 0) {
506 upsdebugx(3, "send: %s", ret ? usb_strerror() : "timeout");
507 return ret;
511 upsdebugx(3, "send: %.*s", (int)strcspn(tmp, "\r"), tmp);
513 /* Read reply */
514 memset(buf, 0, buflen);
516 for (i = 0; (i <= buflen-8) && (strchr(buf, '\r') == NULL); i += ret) {
518 /* Read data in 8-byte chunks */
519 /* ret = usb->get_interrupt(udev, (unsigned char *)&buf[i], 8, 1000); */
520 ret = usb_interrupt_read(udev, 0x81, &buf[i], 8, 1000);
522 /* Any errors here mean that we are unable to read a reply (which will happen after successfully writing a command to the UPS) */
523 if (ret <= 0) {
524 upsdebugx(3, "read: %s", ret ? usb_strerror() : "timeout");
525 return ret;
529 upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf);
530 return i;
533 /* Ippon communication subdriver */
534 static int ippon_command(const char *cmd, char *buf, size_t buflen)
536 char tmp[64];
537 int ret;
538 size_t i;
540 /* Send command */
541 snprintf(tmp, sizeof(tmp), "%s", cmd);
543 for (i = 0; i < strlen(tmp); i += ret) {
545 /* Write data in 8-byte chunks */
546 ret = usb_control_msg(udev, USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE, 0x09, 0x2, 0, &tmp[i], 8, 1000);
548 if (ret <= 0) {
549 upsdebugx(3, "send: %s", (ret != -ETIMEDOUT) ? usb_strerror() : "Connection timed out");
550 return ret;
554 upsdebugx(3, "send: %.*s", (int)strcspn(tmp, "\r"), tmp);
556 /* Read all 64 bytes of the reply in one large chunk */
557 ret = usb_interrupt_read(udev, 0x81, tmp, sizeof(tmp), 1000);
559 /* Any errors here mean that we are unable to read a reply (which will happen after successfully writing a command to the UPS) */
560 if (ret <= 0) {
561 upsdebugx(3, "read: %s", (ret != -ETIMEDOUT) ? usb_strerror() : "Connection timed out");
562 return ret;
565 snprintf(buf, buflen, "%.*s", ret, tmp);
567 upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf);
568 return ret;
571 /* Krauler communication subdriver */
572 static int krauler_command(const char *cmd, char *buf, size_t buflen)
574 /* Still not implemented:
575 * 0x6 T<n> (don't know how to pass the parameter)
576 * 0x68 and 0x69 both cause shutdown after an undefined interval */
577 const struct {
578 const char *str; /* Megatec command */
579 const int index; /* Krauler string index for this command */
580 const char prefix; /* Character to replace the first byte in reply */
581 } command[] = {
582 { "Q1\r", 0x03, '(' },
583 { "F\r", 0x0d, '#' },
584 { "I\r", 0x0c, '#' },
585 { "T\r", 0x04, '\r' },
586 { "TL\r", 0x05, '\r' },
587 { "Q\r", 0x07, '\r' },
588 { "C\r", 0x0b, '\r' },
589 { "CT\r", 0x0b, '\r' },
590 { NULL }
593 int i;
595 upsdebugx(3, "send: %.*s", (int)strcspn(cmd, "\r"), cmd);
597 for (i = 0; command[i].str; i++) {
599 int retry;
601 if (strcmp(cmd, command[i].str)) {
602 continue;
605 for (retry = 0; retry < 10; retry++) {
607 int ret;
609 if (langid_fix != -1) {
610 /* Apply langid_fix value */
611 ret = usb_get_string(udev, command[i].index, langid_fix, buf, buflen);
613 else {
614 ret = usb_get_string_simple(udev, command[i].index, buf, buflen);
617 if (ret <= 0) {
618 upsdebugx(3, "read: %s", ret ? usb_strerror() : "timeout");
619 return ret;
622 /* This may serve in the future */
623 upsdebugx(1, "received %d (%d)", ret, buf[0]);
625 if (langid_fix != -1) {
626 /* Limit this check, at least for now */
627 /* Invalid receive size - message corrupted */
628 if (ret != buf[0]) {
629 upsdebugx(1, "size mismatch: %d / %d", ret, buf[0]);
630 continue;
633 /* Simple unicode -> ASCII inplace conversion
634 * FIXME: this code is at least shared with mge-shut/libshut
635 * Create a common function? */
636 unsigned int di, si, size = buf[0];
637 for (di = 0, si = 2; si < size; si += 2) {
639 if (di >= (buflen - 1))
640 break;
642 if (buf[si + 1]) /* high byte */
643 buf[di++] = '?';
644 else
645 buf[di++] = buf[si];
649 buf[di] = 0;
650 ret = di;
653 /* "UPS No Ack" has a special meaning */
654 if (!strcasecmp(buf, "UPS No Ack")) {
655 upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf);
656 continue;
659 /* Replace the first byte of what we received with the correct one */
660 buf[0] = command[i].prefix;
662 upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf);
663 return ret;
667 return 0;
671 /* Echo the unknown command back */
672 upsdebugx(3, "read: %.*s", (int)strcspn(cmd, "\r"), cmd);
673 return snprintf(buf, buflen, "%s", cmd);
676 static void *cypress_subdriver(USBDevice_t *device)
678 subdriver_command = &cypress_command;
679 return NULL;
682 static void *ippon_subdriver(USBDevice_t *device)
684 subdriver_command = &ippon_command;
685 return NULL;
688 static void *krauler_subdriver(USBDevice_t *device)
690 subdriver_command = &krauler_command;
691 return NULL;
694 static void *phoenix_subdriver(USBDevice_t *device)
696 subdriver_command = &phoenix_command;
697 return NULL;
700 /* USB VendorID/ProductID match - note: rightmost comment is used for naming rules by tools/nut-usbinfo.pl */
701 static usb_device_id_t qx_usb_id[] = {
702 { USB_DEVICE(0x05b8, 0x0000), &cypress_subdriver }, /* Agiler UPS */
703 { USB_DEVICE(0x0001, 0x0000), &krauler_subdriver }, /* Krauler UP-M500VA */
704 { USB_DEVICE(0xffff, 0x0000), &krauler_subdriver }, /* Ablerex 625L USB */
705 { USB_DEVICE(0x0665, 0x5161), &cypress_subdriver }, /* Belkin F6C1200-UNV/Voltronic Power UPSes */
706 { USB_DEVICE(0x06da, 0x0002), &cypress_subdriver }, /* Online Yunto YQ450 */
707 { USB_DEVICE(0x06da, 0x0003), &ippon_subdriver }, /* Mustek Powermust */
708 { USB_DEVICE(0x06da, 0x0004), &cypress_subdriver }, /* Phoenixtec Innova 3/1 T */
709 { USB_DEVICE(0x06da, 0x0005), &cypress_subdriver }, /* Phoenixtec Innova RT */
710 { USB_DEVICE(0x06da, 0x0201), &cypress_subdriver }, /* Phoenixtec Innova T */
711 { USB_DEVICE(0x06da, 0x0601), &phoenix_subdriver }, /* Online Zinto A */
712 { USB_DEVICE(0x0f03, 0x0001), &cypress_subdriver }, /* Unitek Alpha 1200Sx */
713 { USB_DEVICE(0x14f0, 0x00c9), &phoenix_subdriver }, /* GE EP series */
714 /* End of list */
715 { -1, -1, NULL }
718 static int device_match_func(USBDevice_t *hd, void *privdata)
720 if (subdriver_command) {
721 return 1;
724 switch (is_usb_device_supported(qx_usb_id, hd))
726 case SUPPORTED:
727 return 1;
729 case POSSIBLY_SUPPORTED:
730 case NOT_SUPPORTED:
731 default:
732 return 0;
736 static USBDeviceMatcher_t device_matcher = {
737 &device_match_func,
738 NULL,
739 NULL
741 #endif /* QX_USB && !TESTING */
744 /* == Driver functions implementations == */
746 /* Process instant command and take action. */
747 int instcmd(const char *cmdname, const char *extradata)
749 item_t *item;
750 char value[SMALLBUF];
752 if (!strcasecmp(cmdname, "beeper.off")) {
753 /* Compatibility mode for old command */
754 upslogx(LOG_WARNING, "The 'beeper.off' command has been renamed to 'beeper.disable'");
755 return instcmd("beeper.disable", NULL);
758 if (!strcasecmp(cmdname, "beeper.on")) {
759 /* Compatibility mode for old command */
760 upslogx(LOG_WARNING, "The 'beeper.on' command has been renamed to 'beeper.enable'");
761 return instcmd("beeper.enable", NULL);
764 upslogx(LOG_INFO, "%s(%s, %s)", __func__, cmdname, extradata ? extradata : "[NULL]");
766 /* Retrieve item by command name */
767 item = find_nut_info(cmdname, QX_FLAG_CMD, QX_FLAG_SKIP);
769 /* Check for fallback if not found */
770 if (item == NULL) {
772 if (!strcasecmp(cmdname, "load.on")) {
773 return instcmd("load.on.delay", "0");
776 if (!strcasecmp(cmdname, "load.off")) {
777 return instcmd("load.off.delay", "0");
780 if (!strcasecmp(cmdname, "shutdown.return")) {
782 int ret;
784 /* Ensure "ups.start.auto" is set to "yes", if supported */
785 if (dstate_getinfo("ups.start.auto")) {
786 if (setvar("ups.start.auto", "yes") != STAT_SET_HANDLED) {
787 upslogx(LOG_ERR, "%s: FAILED", __func__);
788 return STAT_INSTCMD_FAILED;
792 ret = instcmd("load.on.delay", dstate_getinfo("ups.delay.start"));
793 if (ret != STAT_INSTCMD_HANDLED) {
794 return ret;
797 return instcmd("load.off.delay", dstate_getinfo("ups.delay.shutdown"));
801 if (!strcasecmp(cmdname, "shutdown.stayoff")) {
803 int ret;
805 /* Ensure "ups.start.auto" is set to "no", if supported */
806 if (dstate_getinfo("ups.start.auto")) {
807 if (setvar("ups.start.auto", "no") != STAT_SET_HANDLED) {
808 upslogx(LOG_ERR, "%s: FAILED", __func__);
809 return STAT_INSTCMD_FAILED;
813 ret = instcmd("load.on.delay", "-1");
814 if (ret != STAT_INSTCMD_HANDLED) {
815 return ret;
818 return instcmd("load.off.delay", dstate_getinfo("ups.delay.shutdown"));
822 upsdebugx(2, "%s: command %s unavailable", __func__, cmdname);
823 return STAT_INSTCMD_INVALID;
826 /* If extradata is empty, use the default value from the blazer to NUT table */
827 extradata = extradata ? extradata : item->dfl;
828 snprintf(value, sizeof(value), "%s", extradata ? extradata : "");
830 /* Preprocess command */
831 if (item->preprocess != NULL && item->preprocess(item, value, sizeof(value))) {
832 /* Something went wrong */
833 upslogx(LOG_ERR, "%s: FAILED", __func__);
834 return STAT_INSTCMD_FAILED;
837 /* No preprocess function -> nothing to do with extradata */
838 if (item->preprocess == NULL)
839 snprintf(value, sizeof(value), "%s", "");
841 /* Send the command, get the reply */
842 if (qx_process(item, strlen(value) > 0 ? value : NULL)) {
843 /* Something went wrong */
844 upslogx(LOG_ERR, "%s: FAILED", __func__);
845 return STAT_INSTCMD_FAILED;
848 /* We got a reply from the UPS: either subdriver->accepted (-> command handled) or the command itself echoed back (-> command failed) */
849 if (strlen(item->value) > 0) {
851 if (subdriver->accepted != NULL && !strcasecmp(item->value, subdriver->accepted)) {
852 upslogx(LOG_INFO, "%s: SUCCEED", __func__);
853 /* Set the status so that SEMI_STATIC vars are polled */
854 data_has_changed = TRUE;
855 return STAT_INSTCMD_HANDLED;
858 upslogx(LOG_ERR, "%s: FAILED", __func__);
859 return STAT_INSTCMD_FAILED;
863 /* No reply from the UPS -> command handled */
864 upslogx(LOG_INFO, "%s: SUCCEED", __func__);
865 /* Set the status so that SEMI_STATIC vars are polled */
866 data_has_changed = TRUE;
867 return STAT_INSTCMD_HANDLED;
870 /* Set r/w variable to a value. */
871 int setvar(const char *varname, const char *val)
873 item_t *item;
874 char value[SMALLBUF];
875 st_tree_t *root = (st_tree_t *)dstate_getroot();
876 int ok = 0;
878 /* Retrieve variable */
879 item = find_nut_info(varname, QX_FLAG_SETVAR, QX_FLAG_SKIP);
881 if (item == NULL) {
882 upsdebugx(2, "%s: element %s unavailable", __func__, varname);
883 return STAT_SET_UNKNOWN;
886 /* No NUT variable is available for this item, so we're handling a one-time setvar from ups.conf */
887 if (item->qxflags & QX_FLAG_NONUT) {
889 const char *userval;
891 /* Nothing to do */
892 if (!testvar(item->info_type)) {
893 upsdebugx(2, "%s: nothing to do.. [%s]", __func__, item->info_type);
894 return STAT_SET_HANDLED;
897 userval = getval(item->info_type);
899 upslogx(LOG_INFO, "%s(%s, %s)", __func__, varname, userval ? userval : "[NULL]");
901 snprintf(value, sizeof(value), "%s", userval ? userval : "");
903 /* This item is available in NUT */
904 } else {
906 upslogx(LOG_INFO, "%s(%s, %s)", __func__, varname, strlen(val) ? val : "[NULL]");
908 if (!strlen(val)) {
909 upslogx(LOG_ERR, "%s: value not given for %s", __func__, item->info_type);
910 return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */
913 snprintf(value, sizeof(value), "%s", val);
915 /* Nothing to do */
916 if (!strcasecmp(dstate_getinfo(item->info_type), value)) {
917 upslogx(LOG_INFO, "%s: nothing to do.. [%s]", __func__, item->info_type);
918 return STAT_SET_HANDLED;
923 /* Check if given value is in the range of accepted values (range) */
924 if (item->qxflags & QX_FLAG_RANGE) {
926 int valuetoset, min, max;
928 if (strspn(value, "0123456789 .") != strlen(value)) {
929 upslogx(LOG_ERR, "%s: non numerical value [%s: %s]", __func__, item->info_type, value);
930 return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */
933 valuetoset = strtol(value, NULL, 10);
935 /* No NUT var is available for this item, so take its range from qx2nut table */
936 if (item->qxflags & QX_FLAG_NONUT) {
938 info_rw_t *rvalue;
940 if (!strlen(value)) {
941 upslogx(LOG_ERR, "%s: value not given for %s", __func__, item->info_type);
942 return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */
945 min = max = -1;
947 /* Loop on all existing values */
948 for (rvalue = item->info_rw; rvalue != NULL && strlen(rvalue->value) > 0; rvalue++) {
950 if (rvalue->preprocess && rvalue->preprocess(rvalue->value, sizeof(rvalue->value)))
951 continue;
953 if (min < 0) {
954 min = strtol(rvalue->value, NULL, 10);
955 continue;
958 max = strtol(rvalue->value, NULL, 10);
960 /* valuetoset is in the range */
961 if (min <= valuetoset && valuetoset <= max) {
962 ok = 1;
963 break;
966 min = -1;
967 max = -1;
971 /* We have a NUT var for this item, so check given value against the already set range */
972 } else {
974 const range_t *range = state_getrangelist(root, item->info_type);
976 /* Unable to find tree node for var */
977 if (!range) {
978 upsdebugx(2, "%s: unable to find tree node for %s", __func__, item->info_type);
979 return STAT_SET_UNKNOWN;
982 while (range) {
984 min = range->min;
985 max = range->max;
987 /* valuetoset is in the range */
988 if (min <= valuetoset && valuetoset <= max) {
989 ok = 1;
990 break;
992 range = range->next;
997 if (!ok) {
998 upslogx(LOG_ERR, "%s: value out of range [%s: %s]", __func__, item->info_type, value);
999 return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */
1002 /* Check if given value is in the range of accepted values (enum) */
1003 } else if (item->qxflags & QX_FLAG_ENUM) {
1005 /* No NUT var is available for this item, so take its range from qx2nut table */
1006 if (item->qxflags & QX_FLAG_NONUT) {
1008 info_rw_t *envalue;
1010 if (!strlen(value)) {
1011 upslogx(LOG_ERR, "%s: value not given for %s", __func__, item->info_type);
1012 return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */
1015 /* Loop on all existing values */
1016 for (envalue = item->info_rw; envalue != NULL && strlen(envalue->value) > 0; envalue++) {
1018 if (envalue->preprocess && envalue->preprocess(envalue->value, sizeof(envalue->value)))
1019 continue;
1021 if (strcasecmp(envalue->value, value))
1022 continue;
1024 /* value found */
1025 ok = 1;
1026 break;
1030 /* We have a NUT var for this item, so check given value against the already set range */
1031 } else {
1033 const enum_t *enumlist = state_getenumlist(root, item->info_type);
1035 /* Unable to find tree node for var */
1036 if (!enumlist) {
1037 upsdebugx(2, "%s: unable to find tree node for %s", __func__, item->info_type);
1038 return STAT_SET_UNKNOWN;
1041 while (enumlist) {
1043 /* If this is not the right value, go on to the next */
1044 if (strcasecmp(enumlist->val, value)) {
1045 enumlist = enumlist->next;
1046 continue;
1049 /* value found in enumlist */
1050 ok = 1;
1051 break;
1056 if (!ok) {
1057 upslogx(LOG_ERR, "%s: value out of range [%s: %s]", __func__, item->info_type, value);
1058 return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */
1061 /* Check if given value is not too long (string) */
1062 } else if (item->info_flags & ST_FLAG_STRING) {
1064 const int aux = state_getaux(root, item->info_type);
1066 /* Unable to find tree node for var */
1067 if (aux < 0) {
1068 upsdebugx(2, "%s: unable to find tree node for %s", __func__, item->info_type);
1069 return STAT_SET_UNKNOWN;
1072 if (aux < (int)strlen(value)) {
1073 upslogx(LOG_ERR, "%s: value is too long [%s: %s]", __func__, item->info_type, value);
1074 return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */
1079 /* Preprocess value: from NUT-compliant to UPS-compliant */
1080 if (item->preprocess != NULL && item->preprocess(item, value, sizeof(value))) {
1081 /* Something went wrong */
1082 upslogx(LOG_ERR, "%s: FAILED", __func__);
1083 return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */
1086 /* Handle server side variable */
1087 if (item->qxflags & QX_FLAG_ABSENT) {
1088 upsdebugx(2, "%s: setting server side variable %s", __func__, item->info_type);
1089 dstate_setinfo(item->info_type, "%s", value);
1090 upslogx(LOG_INFO, "%s: SUCCEED", __func__);
1091 return STAT_SET_HANDLED;
1094 /* No preprocess function -> nothing to do with val */
1095 if (item->preprocess == NULL)
1096 snprintf(value, sizeof(value), "%s", "");
1098 /* Actual variable setting */
1099 if (qx_process(item, strlen(value) > 0 ? value : NULL)) {
1100 /* Something went wrong */
1101 upslogx(LOG_ERR, "%s: FAILED", __func__);
1102 return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */
1105 /* We got a reply from the UPS: either subdriver->accepted (-> command handled) or the command itself echoed back (-> command failed) */
1106 if (strlen(item->value) > 0) {
1108 if (subdriver->accepted != NULL && !strcasecmp(item->value, subdriver->accepted)) {
1109 upslogx(LOG_INFO, "%s: SUCCEED", __func__);
1110 /* Set the status so that SEMI_STATIC vars are polled */
1111 data_has_changed = TRUE;
1112 return STAT_SET_HANDLED;
1115 upslogx(LOG_ERR, "%s: FAILED", __func__);
1116 return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */
1120 /* No reply from the UPS -> command handled */
1121 upslogx(LOG_INFO, "%s: SUCCEED", __func__);
1122 /* Set the status so that SEMI_STATIC vars are polled */
1123 data_has_changed = TRUE;
1124 return STAT_SET_HANDLED;
1127 /* Try to shutdown the UPS */
1128 void upsdrv_shutdown(void)
1130 int retry;
1131 item_t *item;
1132 const char *val;
1134 upsdebugx(1, "%s...", __func__);
1136 /* Get user-defined delays */
1138 /* Start delay */
1139 item = find_nut_info("ups.delay.start", 0, QX_FLAG_SKIP);
1141 /* Don't know what happened */
1142 if (!item)
1143 fatalx(EXIT_FAILURE, "Unable to set start delay");
1145 /* Set the default value */
1146 dstate_setinfo(item->info_type, "%s", item->dfl);
1148 /* Set var flags/range/enum */
1149 qx_set_var(item);
1151 /* Retrieve user defined delay settings */
1152 val = getval(QX_VAR_ONDELAY);
1154 if (val && setvar(item->info_type, val) != STAT_SET_HANDLED) {
1155 fatalx(EXIT_FAILURE, "Start delay '%s' out of range", val);
1158 /* Shutdown delay */
1159 item = find_nut_info("ups.delay.shutdown", 0, QX_FLAG_SKIP);
1161 /* Don't know what happened */
1162 if (!item)
1163 fatalx(EXIT_FAILURE, "Unable to set shutdown delay");
1165 /* Set the default value */
1166 dstate_setinfo(item->info_type, "%s", item->dfl);
1168 /* Set var flags/range/enum */
1169 qx_set_var(item);
1171 /* Retrieve user defined delay settings */
1172 val = getval(QX_VAR_OFFDELAY);
1174 if (val && setvar(item->info_type, val) != STAT_SET_HANDLED) {
1175 fatalx(EXIT_FAILURE, "Shutdown delay '%s' out of range", val);
1178 /* Stop pending shutdowns */
1179 if (find_nut_info("shutdown.stop", QX_FLAG_CMD, QX_FLAG_SKIP)) {
1181 for (retry = 1; retry <= MAXTRIES; retry++) {
1183 if (instcmd("shutdown.stop", NULL) != STAT_INSTCMD_HANDLED) {
1184 continue;
1187 break;
1191 if (retry > MAXTRIES) {
1192 upslogx(LOG_NOTICE, "No shutdown pending");
1197 /* Shutdown */
1198 for (retry = 1; retry <= MAXTRIES; retry++) {
1200 if (testvar("stayoff")) {
1202 if (instcmd("shutdown.stayoff", NULL) != STAT_INSTCMD_HANDLED) {
1203 continue;
1206 } else {
1208 if (instcmd("shutdown.return", NULL) != STAT_INSTCMD_HANDLED) {
1209 continue;
1214 fatalx(EXIT_SUCCESS, "Shutting down in %s seconds", dstate_getinfo("ups.delay.shutdown"));
1217 fatalx(EXIT_FAILURE, "Shutdown failed!");
1220 void upsdrv_help(void)
1222 printf("Read The Fine Manual ('man 8 nutdrv_qx')\n");
1225 /* Adding flags/vars */
1226 void upsdrv_makevartable(void)
1228 char temp[SMALLBUF];
1229 int i;
1231 upsdebugx(1, "%s...", __func__);
1233 snprintf(temp, sizeof(temp), "Set shutdown delay, in seconds (default=%s)", DEFAULT_OFFDELAY);
1234 addvar(VAR_VALUE, QX_VAR_OFFDELAY, temp);
1236 snprintf(temp, sizeof(temp), "Set startup delay, in seconds (default=%s)", DEFAULT_ONDELAY);
1237 addvar(VAR_VALUE, QX_VAR_ONDELAY, temp);
1239 addvar(VAR_FLAG, "stayoff", "If invoked the UPS won't return after a shutdown when FSD arises");
1241 snprintf(temp, sizeof(temp), "Set polling frequency, in seconds, to reduce data flow (default=%d)", DEFAULT_POLLFREQ);
1242 addvar(VAR_VALUE, QX_VAR_POLLFREQ, temp);
1244 addvar(VAR_VALUE, "protocol", "Preselect communication protocol (skip autodetection)");
1246 /* battery.{charge,runtime} guesstimation */
1247 addvar(VAR_VALUE, "runtimecal", "Parameters used for runtime calculation");
1248 addvar(VAR_VALUE, "chargetime", "Nominal charge time for UPS battery");
1249 addvar(VAR_VALUE, "idleload", "Minimum load to be used for runtime calculation");
1251 #ifdef QX_USB
1252 addvar(VAR_VALUE, "subdriver", "Serial-over-USB subdriver selection");
1253 addvar(VAR_VALUE, "vendorid", "Regular expression to match UPS Manufacturer numerical ID (4 digits hexadecimal)");
1254 addvar(VAR_VALUE, "productid", "Regular expression to match UPS Product numerical ID (4 digits hexadecimal)");
1256 addvar(VAR_VALUE, "vendor", "Regular expression to match UPS Manufacturer string");
1257 addvar(VAR_VALUE, "product", "Regular expression to match UPS Product string");
1258 addvar(VAR_VALUE, "serial", "Regular expression to match UPS Serial number");
1260 addvar(VAR_VALUE, "bus", "Regular expression to match USB bus name");
1262 addvar(VAR_VALUE, "langid_fix", "Apply the language ID workaround to the krauler subdriver (0x409 or 0x4095)");
1263 #endif /* QX_USB */
1265 #ifdef QX_SERIAL
1266 addvar(VAR_VALUE, "cablepower", "Set cable power for serial interface");
1267 #endif /* QX_SERIAL */
1269 /* Subdrivers flags/vars */
1270 for (i = 0; subdriver_list[i] != NULL; i++) {
1272 if (subdriver_list[i]->makevartable != NULL)
1273 subdriver_list[i]->makevartable();
1278 /* Update UPS status/infos */
1279 void upsdrv_updateinfo(void)
1281 time_t now;
1282 static int retry = 0;
1284 upsdebugx(1, "%s...", __func__);
1286 time(&now);
1288 /* Clear status buffer before beginning */
1289 status_init();
1291 /* Do a full update (polling) every pollfreq or upon data change (i.e. setvar/instcmd) */
1292 if ((now > (lastpoll + pollfreq)) || (data_has_changed == TRUE)) {
1294 upsdebugx(1, "Full update...");
1296 /* Clear ups_status */
1297 ups_status = 0;
1299 alarm_init();
1301 if (qx_ups_walk(QX_WALKMODE_FULL_UPDATE) == FALSE) {
1303 if (retry < MAXTRIES || retry == MAXTRIES) {
1304 upsdebugx(1, "Communications with the UPS lost: status read failed!");
1305 retry++;
1306 } else {
1307 dstate_datastale();
1310 return;
1313 lastpoll = now;
1314 data_has_changed = FALSE;
1316 ups_alarm_set();
1317 alarm_commit();
1319 } else {
1321 upsdebugx(1, "Quick update...");
1323 /* Quick poll data only to see if the UPS is still connected */
1324 if (qx_ups_walk(QX_WALKMODE_QUICK_UPDATE) == FALSE) {
1326 if (retry < MAXTRIES || retry == MAXTRIES) {
1327 upsdebugx(1, "Communications with the UPS lost: status read failed!");
1328 retry++;
1329 } else {
1330 dstate_datastale();
1333 return;
1338 ups_status_set();
1339 status_commit();
1341 if (retry > MAXTRIES) {
1342 upslogx(LOG_NOTICE, "Communications with the UPS re-established");
1345 retry = 0;
1347 dstate_dataok();
1350 /* Initialise data from UPS */
1351 void upsdrv_initinfo(void)
1353 char *val;
1355 upsdebugx(1, "%s...", __func__);
1357 dstate_setinfo("driver.version.data", "%s", subdriver->name);
1359 /* Initialise data */
1360 if (qx_ups_walk(QX_WALKMODE_INIT) == FALSE) {
1361 fatalx(EXIT_FAILURE, "Can't initialise data from the UPS");
1364 /* Init battery guesstimation */
1365 qx_initbattery();
1367 if (dstate_getinfo("ups.delay.start")) {
1369 /* Retrieve user defined delay settings */
1370 val = getval(QX_VAR_ONDELAY);
1372 if (val && setvar("ups.delay.start", val) != STAT_SET_HANDLED) {
1373 fatalx(EXIT_FAILURE, "Start delay '%s' out of range", val);
1378 if (dstate_getinfo("ups.delay.shutdown")) {
1380 /* Retrieve user defined delay settings */
1381 val = getval(QX_VAR_OFFDELAY);
1383 if (val && setvar("ups.delay.shutdown", val) != STAT_SET_HANDLED) {
1384 fatalx(EXIT_FAILURE, "Shutdown delay '%s' out of range", val);
1389 if (!find_nut_info("load.off", QX_FLAG_CMD, QX_FLAG_SKIP) && find_nut_info("load.off.delay", QX_FLAG_CMD, QX_FLAG_SKIP)) {
1390 /* Adds default with a delay value of '0' (= immediate) */
1391 dstate_addcmd("load.off");
1394 if (!find_nut_info("load.on", QX_FLAG_CMD, QX_FLAG_SKIP) && find_nut_info("load.on.delay", QX_FLAG_CMD, QX_FLAG_SKIP)) {
1395 /* Adds default with a delay value of '0' (= immediate) */
1396 dstate_addcmd("load.on");
1399 /* Init polling frequency */
1400 val = getval(QX_VAR_POLLFREQ);
1401 if (val)
1402 pollfreq = strtol(val, NULL, 10);
1404 dstate_setinfo("driver.parameter.pollfreq", "%d", pollfreq);
1406 time(&lastpoll);
1408 /* Install handlers */
1409 upsh.setvar = setvar;
1410 upsh.instcmd = instcmd;
1412 /* Subdriver initinfo */
1413 if (subdriver->initinfo != NULL)
1414 subdriver->initinfo();
1417 /* Open the port and the like and choose the subdriver */
1418 void upsdrv_initups(void)
1420 upsdebugx(1, "%s...", __func__);
1422 #if defined(QX_SERIAL) && defined(QX_USB)
1424 /* Whether the device is connected through USB or serial */
1425 if (
1426 !strcasecmp(dstate_getinfo("driver.parameter.port"), "auto") ||
1427 getval("subdriver") ||
1428 getval("vendorid") ||
1429 getval("productid") ||
1430 getval("vendor") ||
1431 getval("product") ||
1432 getval("serial") ||
1433 getval("bus") ||
1434 getval("langid_fix")
1436 /* USB */
1437 is_usb = 1;
1438 } else {
1439 /* Serial */
1440 is_usb = 0;
1443 #endif /* QX_SERIAL && QX_USB */
1445 /* Serial */
1446 #ifdef QX_SERIAL
1448 #ifdef QX_USB
1449 if (!is_usb) {
1450 #endif /* QX_USB */
1452 #ifndef TESTING
1454 const struct {
1455 const char *val;
1456 const int dtr;
1457 const int rts;
1458 } cablepower[] = {
1459 { "normal", 1, 0 }, /* Default */
1460 { "reverse", 0, 1 },
1461 { "both", 1, 1 },
1462 { "none", 0, 0 },
1463 { NULL }
1466 int i;
1467 const char *val;
1468 struct termios tio;
1470 /* Open and lock the serial port and set the speed to 2400 baud. */
1471 upsfd = ser_open(device_path);
1472 ser_set_speed(upsfd, device_path, B2400);
1474 if (tcgetattr(upsfd, &tio)) {
1475 fatal_with_errno(EXIT_FAILURE, "tcgetattr");
1478 /* Use canonical mode input processing (to read reply line) */
1479 tio.c_lflag |= ICANON; /* Canonical input (erase and kill processing) */
1481 tio.c_cc[VEOF] = _POSIX_VDISABLE;
1482 tio.c_cc[VEOL] = '\r';
1483 tio.c_cc[VERASE] = _POSIX_VDISABLE;
1484 tio.c_cc[VINTR] = _POSIX_VDISABLE;
1485 tio.c_cc[VKILL] = _POSIX_VDISABLE;
1486 tio.c_cc[VQUIT] = _POSIX_VDISABLE;
1487 tio.c_cc[VSUSP] = _POSIX_VDISABLE;
1488 tio.c_cc[VSTART] = _POSIX_VDISABLE;
1489 tio.c_cc[VSTOP] = _POSIX_VDISABLE;
1491 if (tcsetattr(upsfd, TCSANOW, &tio)) {
1492 fatal_with_errno(EXIT_FAILURE, "tcsetattr");
1495 val = getval("cablepower");
1496 for (i = 0; val && cablepower[i].val; i++) {
1498 if (!strcasecmp(val, cablepower[i].val)) {
1499 break;
1503 if (!cablepower[i].val) {
1504 fatalx(EXIT_FAILURE, "Value '%s' not valid for 'cablepower'", val);
1507 ser_set_dtr(upsfd, cablepower[i].dtr);
1508 ser_set_rts(upsfd, cablepower[i].rts);
1510 /* Allow some time to settle for the cablepower */
1511 usleep(100000);
1513 #endif /* TESTING */
1515 #ifdef QX_USB
1516 } else { /* is_usb */
1517 #endif /* QX_USB */
1519 #endif /* QX_SERIAL */
1521 /* USB */
1522 #ifdef QX_USB
1524 #ifndef TESTING
1526 const struct {
1527 const char *name;
1528 int (*command)(const char *cmd, char *buf, size_t buflen);
1529 } usbsubdriver[] = {
1530 { "cypress", &cypress_command },
1531 { "phoenix", &phoenix_command },
1532 { "ippon", &ippon_command },
1533 { "krauler", &krauler_command },
1534 { NULL }
1537 int ret, langid;
1538 char tbuf[255]; /* Some devices choke on size > 255 */
1539 char *regex_array[6];
1541 char *subdrv = getval("subdriver");
1543 regex_array[0] = getval("vendorid");
1544 regex_array[1] = getval("productid");
1545 regex_array[2] = getval("vendor");
1546 regex_array[3] = getval("product");
1547 regex_array[4] = getval("serial");
1548 regex_array[5] = getval("bus");
1550 /* Check for language ID workaround (#1) */
1551 if (getval("langid_fix")) {
1552 /* Skip "0x" prefix and set back to hexadecimal */
1553 if (sscanf(getval("langid_fix") + 2, "%x", &langid_fix) != 1) {
1554 upslogx(LOG_NOTICE, "Error enabling language ID workaround");
1555 } else {
1556 upsdebugx(2, "Language ID workaround enabled (using '0x%x')", langid_fix);
1560 /* Pick up the subdriver name if set explicitly */
1561 if (subdrv) {
1563 int i;
1565 if (!regex_array[0] || !regex_array[1]) {
1566 fatalx(EXIT_FAILURE, "When specifying a subdriver, 'vendorid' and 'productid' are mandatory.");
1569 for (i = 0; usbsubdriver[i].name; i++) {
1571 if (strcasecmp(subdrv, usbsubdriver[i].name)) {
1572 continue;
1575 subdriver_command = usbsubdriver[i].command;
1576 break;
1579 if (!subdriver_command) {
1580 fatalx(EXIT_FAILURE, "Subdriver '%s' not found!", subdrv);
1585 ret = USBNewRegexMatcher(&regex_matcher, regex_array, REG_ICASE | REG_EXTENDED);
1586 switch (ret)
1588 case -1:
1589 fatal_with_errno(EXIT_FAILURE, "USBNewRegexMatcher");
1590 case 0:
1591 break; /* All is well */
1592 default:
1593 fatalx(EXIT_FAILURE, "Invalid regular expression: %s", regex_array[ret]);
1596 /* Link the matchers */
1597 regex_matcher->next = &device_matcher;
1599 ret = usb->open(&udev, &usbdevice, regex_matcher, NULL);
1600 if (ret < 0) {
1601 fatalx(EXIT_FAILURE,
1602 "No supported devices found. Please check your device availability with 'lsusb'\n"
1603 "and make sure you have an up-to-date version of NUT. If this does not help,\n"
1604 "try running the driver with at least 'subdriver', 'vendorid' and 'productid'\n"
1605 "options specified. Please refer to the man page for details about these options\n"
1606 "(man 8 nutdrv_qx).\n");
1609 if (!subdriver_command) {
1610 fatalx(EXIT_FAILURE, "No subdriver selected");
1613 /* Create a new matcher for later reopening */
1614 ret = USBNewExactMatcher(&reopen_matcher, &usbdevice);
1615 if (ret) {
1616 fatal_with_errno(EXIT_FAILURE, "USBNewExactMatcher");
1619 /* Link the matchers */
1620 reopen_matcher->next = regex_matcher;
1622 dstate_setinfo("ups.vendorid", "%04x", usbdevice.VendorID);
1623 dstate_setinfo("ups.productid", "%04x", usbdevice.ProductID);
1625 /* Check for language ID workaround (#2) */
1626 if (langid_fix != -1) {
1627 /* Future improvement:
1628 * Asking for the zero'th index is special - it returns a string descriptor that contains all the language IDs supported by the device.
1629 * Typically there aren't many - often only one.
1630 * The language IDs are 16 bit numbers, and they start at the third byte in the descriptor.
1631 * See USB 2.0 specification, section 9.6.7, for more information on this.
1632 * This should allow automatic application of the workaround */
1633 ret = usb_get_string(udev, 0, 0, tbuf, sizeof(tbuf));
1634 if (ret >= 4) {
1635 langid = tbuf[2] | (tbuf[3] << 8);
1636 upsdebugx(1, "First supported language ID: 0x%x (please report to the NUT maintainer!)", langid);
1640 #endif /* TESTING */
1642 #ifdef QX_SERIAL
1643 } /* is_usb */
1644 #endif /* QX_SERIAL */
1646 #endif /* QX_USB */
1648 /* Choose subdriver */
1649 if (!subdriver_matcher())
1650 fatalx(EXIT_FAILURE, "Device not supported!");
1652 /* Subdriver initups */
1653 if (subdriver->initups != NULL)
1654 subdriver->initups();
1657 /* Close the ports and the like */
1658 void upsdrv_cleanup(void)
1660 upsdebugx(1, "%s...", __func__);
1662 #ifndef TESTING
1664 #ifdef QX_SERIAL
1666 #ifdef QX_USB
1667 if (!is_usb) {
1668 #endif /* QX_USB */
1670 ser_set_dtr(upsfd, 0);
1671 ser_close(upsfd, device_path);
1673 #ifdef QX_USB
1674 } else { /* is_usb */
1675 #endif /* QX_USB */
1677 #endif /* QX_SERIAL */
1679 #ifdef QX_USB
1681 usb->close(udev);
1682 USBFreeExactMatcher(reopen_matcher);
1683 USBFreeRegexMatcher(regex_matcher);
1684 free(usbdevice.Vendor);
1685 free(usbdevice.Product);
1686 free(usbdevice.Serial);
1687 free(usbdevice.Bus);
1689 #ifdef QX_SERIAL
1690 } /* is_usb */
1691 #endif /* QX_SERIAL */
1693 #endif /* QX_USB */
1695 #endif /* TESTING */
1700 /* == Support functions == */
1702 /* Generic command processing function: send a command and read a reply.
1703 * Returns < 0 on error, 0 on timeout and the number of bytes read on success. */
1704 static int qx_command(const char *cmd, char *buf, size_t buflen)
1706 #ifndef TESTING
1708 int ret = -1;
1710 #ifdef QX_USB
1712 #ifdef QX_SERIAL
1713 /* Communication: USB */
1714 if (is_usb) {
1715 #endif /* QX_SERIAL */
1717 if (udev == NULL) {
1718 ret = usb->open(&udev, &usbdevice, reopen_matcher, NULL);
1720 if (ret < 1) {
1721 return ret;
1725 ret = (*subdriver_command)(cmd, buf, buflen);
1727 if (ret >= 0) {
1728 return ret;
1731 switch (ret)
1733 case -EBUSY: /* Device or resource busy */
1734 fatal_with_errno(EXIT_FAILURE, "Got disconnected by another driver");
1736 case -EPERM: /* Operation not permitted */
1737 fatal_with_errno(EXIT_FAILURE, "Permissions problem");
1739 case -EPIPE: /* Broken pipe */
1740 if (usb_clear_halt(udev, 0x81) == 0) {
1741 upsdebugx(1, "Stall condition cleared");
1742 break;
1744 #ifdef ETIME
1745 case -ETIME: /* Timer expired */
1746 #endif /* ETIME */
1747 if (usb_reset(udev) == 0) {
1748 upsdebugx(1, "Device reset handled");
1750 case -ENODEV: /* No such device */
1751 case -EACCES: /* Permission denied */
1752 case -EIO: /* I/O error */
1753 case -ENXIO: /* No such device or address */
1754 case -ENOENT: /* No such file or directory */
1755 /* Uh oh, got to reconnect! */
1756 usb->close(udev);
1757 udev = NULL;
1758 break;
1760 case -ETIMEDOUT: /* Connection timed out */
1761 case -EOVERFLOW: /* Value too large for defined data type */
1762 case -EPROTO: /* Protocol error */
1763 default:
1764 break;
1767 #ifdef QX_SERIAL
1768 /* Communication: serial */
1769 } else { /* !is_usb */
1770 #endif /* QX_SERIAL */
1772 #endif /* QX_USB */
1774 #ifdef QX_SERIAL
1776 ser_flush_io(upsfd);
1778 ret = ser_send(upsfd, "%s", cmd);
1780 if (ret <= 0) {
1781 upsdebugx(3, "send: %s", ret ? strerror(errno) : "timeout");
1782 return ret;
1785 upsdebugx(3, "send: '%.*s'", (int)strcspn(cmd, "\r"), cmd);
1787 ret = ser_get_buf(upsfd, buf, buflen, SER_WAIT_SEC, 0);
1789 if (ret <= 0) {
1790 upsdebugx(3, "read: %s", ret ? strerror(errno) : "timeout");
1791 return ret;
1794 upsdebugx(3, "read: '%.*s'", (int)strcspn(buf, "\r"), buf);
1796 #ifdef QX_USB
1797 } /* !is_usb */
1798 #endif /* QX_USB */
1800 #endif /* QX_SERIAL */
1802 return ret;
1804 #else /* TESTING */
1806 testing_t *testing = subdriver->testing;
1807 int i;
1809 memset(buf, 0, buflen);
1811 upsdebugx(3, "send: '%.*s'", (int)strcspn(cmd, "\r"), cmd);
1813 for (i = 0; cmd && testing[i].cmd; i++) {
1815 if (strcasecmp(cmd, testing[i].cmd)) {
1816 continue;
1819 upsdebugx(3, "read: '%.*s'", (int)strcspn(testing[i].answer, "\r"), testing[i].answer);
1820 return snprintf(buf, buflen, "%s", testing[i].answer);
1824 /* If the driver expects some kind of reply in case of error.. */
1825 if (subdriver->rejected != NULL) {
1827 /* ..fulfill its expectations.. */
1828 upsdebugx(3, "read: '%.*s'", (int)strcspn(subdriver->rejected, "\r"), subdriver->rejected);
1829 return snprintf(buf, buflen, "%s", subdriver->rejected);
1831 /* ..otherwise.. */
1832 } else {
1834 /* ..echo back the command */
1835 upsdebugx(3, "read: '%.*s'", (int)strcspn(cmd, "\r"), cmd);
1836 return snprintf(buf, buflen, "%s", cmd);
1840 #endif /* TESTING */
1843 /* Update ups_status to remember this status item.
1844 * Interpretation is done in ups_status_set(). */
1845 void update_status(const char *value)
1847 status_lkp_t *status_item;
1848 int clear = 0;
1850 upsdebugx(5, "%s: %s", __func__, value);
1852 if (*value == '!') {
1853 value++;
1854 clear = 1;
1857 for (status_item = status_info; status_item->status_str != NULL ; status_item++) {
1859 if (strcasecmp(status_item->status_str, value))
1860 continue;
1862 if (clear) {
1863 ups_status &= ~status_item->status_mask;
1864 } else {
1865 ups_status |= status_item->status_mask;
1868 return;
1871 upsdebugx(5, "%s: Warning! %s not in list of known values", __func__, value);
1874 /* Choose subdriver */
1875 static int subdriver_matcher(void)
1877 const char *protocol = getval("protocol");
1878 int i;
1880 /* Select the subdriver for this device */
1881 for (i = 0; subdriver_list[i] != NULL; i++) {
1883 int j;
1885 /* If protocol is set in ups.conf, use it */
1886 if (protocol) {
1888 char subdrv_name[SMALLBUF];
1890 /* Get rid of subdriver version */
1891 snprintf(subdrv_name, sizeof(subdrv_name), "%.*s", (int)strcspn(subdriver_list[i]->name, " "), subdriver_list[i]->name);
1893 if (strcasecmp(subdrv_name, protocol)) {
1894 upsdebugx(2, "Skipping protocol %s", subdriver_list[i]->name);
1895 continue;
1900 /* Give every subdriver some tries */
1901 for (j = 0; j < MAXTRIES; j++) {
1903 subdriver = subdriver_list[i];
1905 if (subdriver->claim()) {
1906 break;
1909 subdriver = NULL;
1913 if (subdriver != NULL)
1914 break;
1918 if (!subdriver) {
1919 upslogx(LOG_ERR, "Device not supported!");
1920 return 0;
1923 upslogx(LOG_INFO, "Using protocol: %s", subdriver->name);
1925 return 1;
1928 /* Set vars boundaries */
1929 static void qx_set_var(item_t *item)
1931 if (!(item->qxflags & QX_FLAG_NONUT))
1932 dstate_setflags(item->info_type, item->info_flags);
1934 /* Set max length for strings, if needed */
1935 if (item->info_flags & ST_FLAG_STRING && !(item->qxflags & QX_FLAG_NONUT))
1936 dstate_setaux(item->info_type, strtol(item->info_rw[0].value, NULL, 10));
1938 /* Set enum list */
1939 if (item->qxflags & QX_FLAG_ENUM) {
1941 info_rw_t *envalue;
1942 char buf[LARGEBUF] = "";
1944 /* Loop on all existing values */
1945 for (envalue = item->info_rw; envalue != NULL && strlen(envalue->value) > 0; envalue++) {
1947 if (envalue->preprocess && envalue->preprocess(envalue->value, sizeof(envalue->value)))
1948 continue;
1950 /* This item is not available yet in NUT, so publish these data in the logs */
1951 if (item->qxflags & QX_FLAG_NONUT) {
1953 snprintfcat(buf, sizeof(buf), " %s", envalue->value);
1955 /* This item is available in NUT, add its enum to the variable */
1956 } else {
1958 dstate_addenum(item->info_type, "%s", envalue->value);
1964 if (item->qxflags & QX_FLAG_NONUT)
1965 upslogx(LOG_INFO, "%s, settable values:%s", item->info_type, strlen(buf) > 0 ? buf : " none");
1969 /* Set range */
1970 if (item->qxflags & QX_FLAG_RANGE) {
1972 info_rw_t *rvalue, *from = NULL, *to = NULL;
1973 int ok = 0;
1975 /* Loop on all existing values */
1976 for (rvalue = item->info_rw; rvalue != NULL && strlen(rvalue->value) > 0; rvalue++) {
1978 if (rvalue->preprocess && rvalue->preprocess(rvalue->value, sizeof(rvalue->value)))
1979 continue;
1981 if (!from) {
1982 from = rvalue;
1983 continue;
1986 to = rvalue;
1988 /* This item is not available yet in NUT, so publish these data in the logs */
1989 if (item->qxflags & QX_FLAG_NONUT) {
1991 upslogx(LOG_INFO, "%s, settable range: %s..%s", item->info_type, from->value, to->value);
1992 ok++;
1994 /* This item is available in NUT, add its range to the variable */
1995 } else {
1997 dstate_addrange(item->info_type, strtol(from->value, NULL, 10), strtol(to->value, NULL, 10));
2001 from = NULL;
2002 to = NULL;
2006 /* This item is not available yet in NUT and we weren't able to get its range; let people know it */
2007 if ((item->qxflags & QX_FLAG_NONUT) && !ok)
2008 upslogx(LOG_INFO, "%s, settable range: none", item->info_type);
2013 /* Walk UPS variables and set elements of the qx2nut array. */
2014 static bool_t qx_ups_walk(walkmode_t mode)
2016 item_t *item;
2017 int retcode;
2019 /* Clear batt.{chrg,runt}.act for guesstimation */
2020 if (mode == QX_WALKMODE_FULL_UPDATE) {
2021 batt.runt.act = -1;
2022 batt.chrg.act = -1;
2025 /* 3 modes: QX_WALKMODE_INIT, QX_WALKMODE_QUICK_UPDATE and QX_WALKMODE_FULL_UPDATE */
2027 /* Device data walk */
2028 for (item = subdriver->qx2nut; item->info_type != NULL; item++) {
2030 /* Skip this item */
2031 if (item->qxflags & QX_FLAG_SKIP)
2032 continue;
2034 upsdebugx(10, "%s: processing: %s", __func__, item->info_type);
2036 /* Filter data according to mode */
2037 switch (mode)
2039 /* Device capabilities enumeration */
2040 case QX_WALKMODE_INIT:
2042 /* Special case for handling server side variables */
2043 if (item->qxflags & QX_FLAG_ABSENT) {
2045 /* Already set */
2046 if (dstate_getinfo(item->info_type))
2047 continue;
2049 dstate_setinfo(item->info_type, "%s", item->dfl);
2051 /* Set var flags/range/enum */
2052 qx_set_var(item);
2054 continue;
2057 /* Allow duplicates for these NUT variables */
2058 if (!strncmp(item->info_type, "ups.alarm", 9) || !strncmp(item->info_type, "ups.status", 10))
2059 break;
2061 /* This one doesn't exist yet */
2062 if (dstate_getinfo(item->info_type) == NULL)
2063 break;
2065 continue;
2067 case QX_WALKMODE_QUICK_UPDATE:
2069 /* Quick update only deals with status and alarms! */
2070 if (!(item->qxflags & QX_FLAG_QUICK_POLL))
2071 continue;
2073 break;
2075 case QX_WALKMODE_FULL_UPDATE:
2077 /* These don't need polling after initinfo() */
2078 if (item->qxflags & (QX_FLAG_ABSENT | QX_FLAG_CMD | QX_FLAG_SETVAR | QX_FLAG_STATIC))
2079 continue;
2081 /* These need to be polled after user changes (setvar / instcmd) */
2082 if ((item->qxflags & QX_FLAG_SEMI_STATIC) && (data_has_changed == FALSE))
2083 continue;
2085 break;
2087 default:
2089 fatalx(EXIT_FAILURE, "%s: unknown update mode!", __func__);
2093 /* Instant commands */
2094 if (item->qxflags & QX_FLAG_CMD) {
2095 dstate_addcmd(item->info_type);
2096 continue;
2099 /* Setvars */
2100 if (item->qxflags & QX_FLAG_SETVAR) {
2102 if (item->qxflags & QX_FLAG_NONUT) {
2103 setvar(item->info_type, NULL);
2104 item->qxflags |= QX_FLAG_SKIP;
2107 continue;
2111 /* Check whether the previous item uses the same command and then use its answer, if available.. */
2112 if (strlen(previous_item.command) > 0 && strlen(previous_item.answer) > 0 && !strcasecmp(previous_item.command, item->command)) {
2114 snprintf(item->answer, sizeof(item->answer), "%s", previous_item.answer);
2116 /* Process the answer */
2117 retcode = qx_process_answer(item, strlen(item->answer));
2119 /* ..otherwise: execute command to get answer from the UPS */
2120 } else
2121 retcode = qx_process(item, NULL);
2123 /* Record item as previous_item */
2124 snprintf(previous_item.command, sizeof(previous_item.command), "%s", item->command);
2125 snprintf(previous_item.answer, sizeof(previous_item.answer), "%s", item->answer);
2127 if (retcode) {
2129 if (item->qxflags & QX_FLAG_QUICK_POLL)
2130 return FALSE;
2132 if (mode == QX_WALKMODE_INIT)
2133 /* Skip this item from now on */
2134 item->qxflags |= QX_FLAG_SKIP;
2136 /* Clear data from the item */
2137 snprintf(item->answer, sizeof(item->answer), "%s", "");
2138 snprintf(item->value, sizeof(item->value), "%s", "");
2140 /* Don't know what happened, try again later... */
2141 continue;
2145 /* Process the value we got back (set status bits and set the value of other parameters) */
2146 retcode = ups_infoval_set(item);
2148 /* Clear data from the item */
2149 snprintf(item->answer, sizeof(item->answer), "%s", "");
2150 snprintf(item->value, sizeof(item->value), "%s", "");
2152 /* Uh-oh! Some error! */
2153 if (retcode == -1)
2154 continue;
2156 /* Set var flags/range/enum (not for ups.{alarm.status}, hence the retcode check) */
2157 if (retcode && mode == QX_WALKMODE_INIT) {
2158 qx_set_var(item);
2163 /* Clear data from previous_item */
2164 snprintf(previous_item.command, sizeof(previous_item.command), "%s", "");
2165 snprintf(previous_item.answer, sizeof(previous_item.answer), "%s", "");
2167 /* Update battery guesstimation */
2168 if (mode == QX_WALKMODE_FULL_UPDATE && (batt.runt.act == -1 || batt.chrg.act == -1)) {
2170 if (getval("runtimecal")) {
2172 time_t battery_now;
2174 time(&battery_now);
2176 /* OL */
2177 if (ups_status & STATUS(OL)) {
2179 batt.runt.est += batt.runt.nom * difftime(battery_now, battery_lastpoll) / batt.chrg.time;
2180 if (batt.runt.est > batt.runt.nom) {
2181 batt.runt.est = batt.runt.nom;
2184 /* OB */
2185 } else {
2187 batt.runt.est -= load.eff * difftime(battery_now, battery_lastpoll);
2188 if (batt.runt.est < 0) {
2189 batt.runt.est = 0;
2194 if (batt.chrg.act == -1)
2195 dstate_setinfo("battery.charge", "%.0f", 100 * batt.runt.est / batt.runt.nom);
2197 if (batt.runt.act == -1 && !qx_load())
2198 dstate_setinfo("battery.runtime", "%.0f", batt.runt.est / load.eff);
2200 battery_lastpoll = battery_now;
2202 } else {
2204 qx_battery();
2209 return TRUE;
2212 /* Convert the local status information to NUT format and set NUT alarms. */
2213 static void ups_alarm_set(void)
2215 if (ups_status & STATUS(RB)) {
2216 alarm_set("Replace battery!");
2218 if (ups_status & STATUS(FSD)) {
2219 alarm_set("Shutdown imminent!");
2223 /* Convert the local status information to NUT format and set NUT status. */
2224 static void ups_status_set(void)
2226 if (ups_status & STATUS(OL)) {
2227 status_set("OL"); /* On line */
2228 } else {
2229 status_set("OB"); /* On battery */
2231 if (ups_status & STATUS(DISCHRG)) {
2232 status_set("DISCHRG"); /* Discharging */
2234 if (ups_status & STATUS(CHRG)) {
2235 status_set("CHRG"); /* Charging */
2237 if (ups_status & STATUS(LB)) {
2238 status_set("LB"); /* Low battery */
2240 if (ups_status & STATUS(OVER)) {
2241 status_set("OVER"); /* Overload */
2243 if (ups_status & STATUS(RB)) {
2244 status_set("RB"); /* Replace battery */
2246 if (ups_status & STATUS(TRIM)) {
2247 status_set("TRIM"); /* SmartTrim */
2249 if (ups_status & STATUS(BOOST)) {
2250 status_set("BOOST"); /* SmartBoost */
2252 if (ups_status & STATUS(BYPASS)) {
2253 status_set("BYPASS"); /* On bypass */
2255 if (ups_status & STATUS(OFF)) {
2256 status_set("OFF"); /* UPS is off */
2258 if (ups_status & STATUS(CAL)) {
2259 status_set("CAL"); /* Calibration */
2261 if (ups_status & STATUS(FSD)) {
2262 status_set("FSD"); /* Forced shutdown */
2266 /* Find element definition in qx2nut array by NUT varname optionally filtered by its qxflags:
2267 * - 'flag': flags that have to be set in the item, i.e. if one of the flags is absent in the item it won't be returned
2268 * - 'noflag': flags that have to be absent in the item, i.e. if at least one of the flags is set in the item it won't be returned */
2269 item_t *find_nut_info(const char *varname, const unsigned long flag, const unsigned long noflag)
2271 item_t *item;
2273 for (item = subdriver->qx2nut; item->info_type != NULL; item++) {
2275 if (strcasecmp(item->info_type, varname))
2276 continue;
2278 if (flag && ((item->qxflags & flag) != flag))
2279 continue;
2281 if (noflag && (item->qxflags & noflag))
2282 continue;
2284 return item;
2287 upsdebugx(2, "%s: info type %s not found", __func__, varname);
2288 return NULL;
2291 /* Process the answer we got back from the UPS
2292 * Return -1 on errors, 0 on success */
2293 static int qx_process_answer(item_t *item, const int len)
2295 /* Query rejected by the UPS */
2296 if (subdriver->rejected && !strcasecmp(item->answer, subdriver->rejected)) {
2297 upsdebugx(2, "%s: query rejected by the UPS (%s)", __func__, item->info_type);
2298 return -1;
2301 /* Short reply */
2302 if (item->answer_len && len < item->answer_len) {
2303 upsdebugx(2, "%s: short reply (%s)", __func__, item->info_type);
2304 return -1;
2307 /* Wrong leading character */
2308 if (item->leading && item->answer[0] != item->leading) {
2309 upsdebugx(2, "%s: %s - invalid start character [%02x], expected [%02x]", __func__, item->info_type, item->answer[0], item->leading);
2310 return -1;
2313 /* Get value */
2314 if (strlen(item->answer)) {
2315 snprintf(item->value, sizeof(item->value), "%.*s", item->to ? 1 + item->to - item->from : (int)strcspn(item->answer, "\r") - item->from, item->answer + item->from);
2316 } else {
2317 snprintf(item->value, sizeof(item->value), "%s", "");
2320 return 0;
2323 /* Send the command to the UPS and process the reply.
2324 * Return -1 on errors, 0 on success */
2325 int qx_process(item_t *item, const char *command)
2327 char buf[SMALLBUF] = "";
2329 /* Send the command */
2330 int len = qx_command(command ? command : item->command, buf, sizeof(buf));
2332 snprintf(item->answer, sizeof(item->answer), "%s", buf);
2334 /* Process the answer to get the value */
2335 return qx_process_answer(item, len);
2338 /* Process the value we got back (set status bits and set the value of other parameters). */
2339 /* Return -1 on failure, 0 for a status update and 1 in all other cases */
2340 int ups_infoval_set(item_t *item)
2342 char value[SMALLBUF] = "";
2344 /* Item need to be preprocessed? */
2345 if (item->preprocess != NULL){
2347 /* Process the value returned by the UPS to NUT standards */
2348 if (item->preprocess(item, value, sizeof(value))) {
2349 upsdebugx(4, "%s: failed to preprocess value [%s: %s]", __func__, item->info_type, item->value);
2350 return -1;
2353 /* Deal with status items */
2354 if (!strncmp(item->info_type, "ups.status", 10)) {
2355 if (strlen(value) > 0)
2356 update_status(value);
2357 return 0;
2360 /* Deal with alarm items */
2361 if (!strncmp(item->info_type, "ups.alarm", 9)) {
2362 if (strlen(value) > 0)
2363 alarm_set(value);
2364 return 0;
2367 } else {
2369 snprintf(value, sizeof(value), "%s", item->value);
2371 /* Cover most of the cases: either left/right filled with hashes, spaces or a mix of both */
2372 if (item->qxflags & QX_FLAG_TRIM) {
2374 char buf[SMALLBUF];
2376 snprintf(buf, sizeof(buf), "%s", ltrim(value, ' '));
2377 snprintf(value, sizeof(value), "%s", buf);
2379 snprintf(buf, sizeof(buf), "%s", rtrim(value, ' '));
2380 snprintf(value, sizeof(value), "%s", buf);
2382 snprintf(buf, sizeof(buf), "%s", ltrim(value, '#'));
2383 snprintf(value, sizeof(value), "%s", buf);
2385 snprintf(buf, sizeof(buf), "%s", rtrim(value, '#'));
2386 snprintf(value, sizeof(value), "%s", buf);
2388 snprintf(buf, sizeof(buf), "%s", ltrim(value, ' '));
2389 snprintf(value, sizeof(value), "%s", buf);
2391 snprintf(buf, sizeof(buf), "%s", rtrim(value, ' '));
2392 snprintf(value, sizeof(value), "%s", buf);
2394 snprintf(buf, sizeof(buf), "%s", ltrim(value, '#'));
2395 snprintf(value, sizeof(value), "%s", buf);
2397 snprintf(buf, sizeof(buf), "%s", rtrim(value, '#'));
2398 snprintf(value, sizeof(value), "%s", buf);
2402 if (strcasecmp(item->dfl, "%s")) {
2404 if (strspn(value, "0123456789 .") != strlen(value)) {
2405 upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, value);
2406 return -1;
2409 snprintf(value, sizeof(value), item->dfl, strtod(value, NULL));
2414 if (item->qxflags & QX_FLAG_NONUT) {
2415 upslogx(LOG_INFO, "%s: %s", item->info_type, value);
2416 return 1;
2419 if (!strlen(value)) {
2420 upsdebugx(1, "%s: non significant value [%s]", __func__, item->info_type);
2421 return -1;
2424 dstate_setinfo(item->info_type, "%s", value);
2426 /* Fill batt.{chrg,runt}.act for guesstimation */
2427 if (!strcasecmp(item->info_type, "battery.charge"))
2428 batt.chrg.act = strtol(value, NULL, 10);
2429 else if (!strcasecmp(item->info_type, "battery.runtime"))
2430 batt.runt.act = strtol(value, NULL, 10);
2432 return 1;
2435 /* Return actual status */
2436 int qx_status(void)
2438 return ups_status;