1 /* nutdrv_qx.c - Driver for USB and serial UPS units with Q* protocols
2 *
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>
19 * Masterguard additions
20 * 2020-2021 Edgar Fuß, Mathematisches Institut der Universität Bonn <ef@math.uni-bonn.de>
21 *
22 * This program is free software; you can redistribute it and/or modify
23 * it under the terms of the GNU General Public License as published by
24 * the Free Software Foundation; either version 2 of the License, or
25 * (at your option) any later version.
26 *
27 * This program is distributed in the hope that it will be useful,
28 * but WITHOUT ANY WARRANTY; without even the implied warranty of
29 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
30 * GNU General Public License for more details.
31 *
32 * You should have received a copy of the GNU General Public License
33 * along with this program; if not, write to the Free Software
34 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
35 *
36 */
37
38 #define DRIVER_VERSION "0.30"
39
40 #include "config.h"
41 #include "main.h"
42 #include "attribute.h"
43 #include "nut_float.h"
44 #include "nut_stdint.h"
45
46 /* note: QX_USB/QX_SERIAL set through Makefile */
47 #ifdef QX_USB
48 #include "libusb.h"
49 #include "usb-common.h"
50
51 #ifdef QX_SERIAL
52 #define DRIVER_NAME "Generic Q* USB/Serial driver"
53 #else
54 #define DRIVER_NAME "Generic Q* USB driver"
55 #endif /* QX_SERIAL */
56 #else
57 #define DRIVER_NAME "Generic Q* Serial driver"
58 #endif /* QX_USB */
59
60 #ifdef QX_SERIAL
61 #include "serial.h"
62 #define SER_WAIT_SEC 1 /* 3 seconds for Best UPS */
63 #endif /* QX_SERIAL */
64
65 #include "nutdrv_qx.h"
66
67 /* == Subdrivers == */
68 /* Include all known subdrivers */
69 #include "nutdrv_qx_bestups.h"
70 #include "nutdrv_qx_hunnox.h"
71 #include "nutdrv_qx_mecer.h"
72 #include "nutdrv_qx_megatec.h"
73 #include "nutdrv_qx_megatec-old.h"
74 #include "nutdrv_qx_mustek.h"
75 #include "nutdrv_qx_q1.h"
76 #include "nutdrv_qx_voltronic.h"
77 #include "nutdrv_qx_voltronic-qs.h"
78 #include "nutdrv_qx_voltronic-qs-hex.h"
79 #include "nutdrv_qx_zinto.h"
80 #include "nutdrv_qx_masterguard.h"
81
82 /* Reference list of available subdrivers */
83 static subdriver_t *subdriver_list[] = {
84 &voltronic_subdriver,
85 &voltronic_qs_subdriver,
86 &voltronic_qs_hex_subdriver,
87 &mustek_subdriver,
88 &megatec_old_subdriver,
89 &bestups_subdriver,
90 &mecer_subdriver,
91 &megatec_subdriver,
92 &zinto_subdriver,
93 &masterguard_subdriver,
94 &hunnox_subdriver,
95 /* Fallback Q1 subdriver */
96 &q1_subdriver,
97 NULL
98 };
99
100
101 /* == Driver description structure == */
102 upsdrv_info_t upsdrv_info = {
103 DRIVER_NAME,
104 DRIVER_VERSION,
105 "Daniele Pezzini <hyouko@gmail.com>" \
106 "Arnaud Quette <arnaud.quette@gmail.com>" \
107 "John Stamp <kinsayder@hotmail.com>" \
108 "Peter Selinger <selinger@users.sourceforge.net>" \
109 "Arjen de Korte <adkorte-guest@alioth.debian.org>" \
110 "Edgar Fuß <ef@math.uni-bonn.de>",
111 DRV_BETA,
112 #ifdef QX_USB
113 { &comm_upsdrv_info, NULL }
114 #else
115 { NULL }
116 #endif /* QX_USB */
117 };
118
119
120 /* == Data walk modes == */
121 typedef enum {
122 QX_WALKMODE_INIT = 0,
123 QX_WALKMODE_QUICK_UPDATE,
124 QX_WALKMODE_FULL_UPDATE
125 } walkmode_t;
126
127
128 /* == Global vars == */
129 /* Pointer to the active subdriver object (changed in subdriver_matcher() function) */
130 static subdriver_t *subdriver = NULL;
131
132 static long pollfreq = DEFAULT_POLLFREQ;
133 static unsigned int ups_status = 0;
134 static bool_t data_has_changed = FALSE; /* for SEMI_STATIC data polling */
135
136 static time_t lastpoll; /* Timestamp the last polling */
137
138 #if defined(QX_USB) && !defined(TESTING)
139 static int hunnox_step = 0;
140 #endif /* QX_USB && !TESTING */
141
142 #if defined(QX_USB) && defined(QX_SERIAL)
143 static int is_usb = 0; /* Whether the device is connected through USB (1) or serial (0) */
144 #endif /* QX_USB && QX_SERIAL */
145
146 static struct {
147 char command[SMALLBUF]; /* Command sent to the UPS to get answer/to execute an instant command */
148 char answer[SMALLBUF]; /* Answer from the UPS, filled at runtime */
149 } previous_item = { "", "" }; /* Hold the values of the item processed just before the actual one */
150
151
152 /* == Support functions == */
153 static int subdriver_matcher(void);
154 static ssize_t qx_command(const char *cmd, char *buf, size_t buflen);
155 static int qx_process_answer(item_t *item, const size_t len); /* returns just 0 or -1 */
156 static bool_t qx_ups_walk(walkmode_t mode);
157 static void ups_status_set(void);
158 static void ups_alarm_set(void);
159 static void qx_set_var(item_t *item);
160
161
162 /* == Struct & data for status processing == */
163 typedef struct {
164 const char *status_str; /* UPS status string */
165 const unsigned int status_mask; /* UPS status mask */
166 } status_lkp_t;
167
168 static status_lkp_t status_info[] = {
169 /* Map status strings to bit masks */
170 { "OL", STATUS(OL) },
171 { "LB", STATUS(LB) },
172 { "RB", STATUS(RB) },
173 { "CHRG", STATUS(CHRG) },
174 { "DISCHRG", STATUS(DISCHRG) },
175 { "BYPASS", STATUS(BYPASS) },
176 { "CAL", STATUS(CAL) },
177 { "OFF", STATUS(OFF) },
178 { "OVER", STATUS(OVER) },
179 { "TRIM", STATUS(TRIM) },
180 { "BOOST", STATUS(BOOST) },
181 { "FSD", STATUS(FSD) },
182 { NULL, 0 },
183 };
184
185
186 /* == battery.{charge,runtime} guesstimation == */
187 /* Support functions */
188 static int qx_battery(void);
189 static int qx_load(void);
190 static void qx_initbattery(void);
191
192 /* Battery data */
193 static struct {
194 double packs; /* Battery voltage multiplier */
195 struct {
196 double act; /* Actual runtime on battery */
197 double nom; /* Nominal runtime on battery (full load) */
198 double est; /* Estimated runtime remaining (full load) */
199 double exp; /* Load exponent */
200 } runt;
201 struct {
202 double act; /* Actual battery voltage */
203 double high; /* Battery float voltage */
204 double nom; /* Nominal battery voltage */
205 double low; /* Battery low voltage */
206 } volt;
207 struct {
208 double act; /* Actual battery charge */
209 long time; /* Recharge time from empty to full */
210 } chrg;
211 } batt = { 1, { -1, -1, 0, 0 }, { -1, -1, -1, -1 }, { -1, 43200 } };
212
213 /* Load data */
214 static struct {
215 double act; /* Actual load (reported by the UPS) */
216 double low; /* Idle load */
217 double eff; /* Effective load */
218 } load = { 0, 0.1, 1 };
219
220 static time_t battery_lastpoll = 0;
221
222 /* Fill batt.volt.act and guesstimate the battery charge if it isn't already available. */
qx_battery(void)223 static int qx_battery(void)
224 {
225 const char *val = dstate_getinfo("battery.voltage");
226
227 if (!val) {
228 upsdebugx(2, "%s: unable to get battery.voltage", __func__);
229 return -1;
230 }
231
232 batt.volt.act = batt.packs * strtod(val, NULL);
233
234 if (d_equal(batt.chrg.act, -1) && batt.volt.low > 0 && batt.volt.high > batt.volt.low) {
235
236 batt.chrg.act = 100 * (batt.volt.act - batt.volt.low) / (batt.volt.high - batt.volt.low);
237
238 if (batt.chrg.act < 0) {
239 batt.chrg.act = 0;
240 }
241
242 if (batt.chrg.act > 100) {
243 batt.chrg.act = 100;
244 }
245
246 dstate_setinfo("battery.charge", "%.0f", batt.chrg.act);
247
248 }
249
250 return 0;
251 }
252
253 /* Load for battery.{charge,runtime} from runtimecal */
qx_load(void)254 static int qx_load(void)
255 {
256 const char *val = dstate_getinfo("ups.load");
257
258 if (!val) {
259 upsdebugx(2, "%s: unable to get ups.load", __func__);
260 return -1;
261 }
262
263 load.act = strtod(val, NULL);
264
265 load.eff = pow(load.act / 100, batt.runt.exp);
266
267 if (load.eff < load.low) {
268 load.eff = load.low;
269 }
270
271 return 0;
272 }
273
274 /* Guesstimation: init */
qx_initbattery(void)275 static void qx_initbattery(void)
276 {
277 if (!dstate_getinfo("battery.charge") || !dstate_getinfo("battery.runtime")) {
278
279 const char *val;
280
281 val = dstate_getinfo("battery.voltage.high");
282 if (val) {
283 batt.volt.high = strtod(val, NULL);
284 }
285
286 val = dstate_getinfo("battery.voltage.low");
287 if (val) {
288 batt.volt.low = strtod(val, NULL);
289 }
290
291 val = dstate_getinfo("battery.voltage.nominal");
292 if (val) {
293 batt.volt.nom = strtod(val, NULL);
294 }
295
296 /* 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! */
297 if ( (!d_equal(batt.volt.nom, -1)) && (d_equal(batt.volt.low, -1) || d_equal(batt.volt.high, -1))) {
298
299 upslogx(LOG_INFO, "No values for battery high/low voltages");
300
301 /* Basic formula, which should cover most cases */
302 batt.volt.low = 104 * batt.volt.nom / 120;
303 batt.volt.high = 130 * batt.volt.nom / 120;
304
305 /* Publish these data too */
306 dstate_setinfo("battery.voltage.low", "%.2f", batt.volt.low);
307 dstate_setinfo("battery.voltage.high", "%.2f", batt.volt.high);
308
309 upslogx(LOG_INFO, "Using 'guesstimation' (low: %f, high: %f)!", batt.volt.low, batt.volt.high);
310
311 }
312
313 val = dstate_getinfo("battery.packs");
314 if (val && (strspn(val, "0123456789 .") == strlen(val))) {
315 batt.packs = strtod(val, NULL);
316 } else {
317
318 /* qx_battery -> batt.volt.act */
319 if (!qx_battery() && (!d_equal(batt.volt.nom, -1))) {
320
321 const double packs[] = { 120, 100, 80, 60, 48, 36, 30, 24, 18, 12, 8, 6, 4, 3, 2, 1, 0.5, -1 };
322 int i;
323
324 /* The battery voltage will quickly return to at least the nominal value after discharging them.
325 * For overlapping battery.voltage.low/high ranges therefor choose the one with the highest multiplier. */
326 for (i = 0; packs[i] > 0; i++) {
327
328 if (packs[i] * batt.volt.act > 1.2 * batt.volt.nom) {
329 continue;
330 }
331
332 if (packs[i] * batt.volt.act < 0.8 * batt.volt.nom) {
333 upslogx(LOG_INFO, "Can't autodetect number of battery packs [%.0f/%.2f]", batt.volt.nom, batt.volt.act);
334 break;
335 }
336
337 batt.packs = packs[i];
338 break;
339
340 }
341
342 } else {
343 upslogx(LOG_INFO, "Can't autodetect number of battery packs [%.0f/%.2f]", batt.volt.nom, batt.volt.act);
344 }
345
346 }
347
348 /* Update batt.{chrg,volt}.act */
349 qx_battery();
350
351 val = getval("runtimecal");
352 if (val) {
353
354 double rh, lh, rl, ll;
355
356 time(&battery_lastpoll);
357
358 if (sscanf(val, "%lf,%lf,%lf,%lf", &rh, &lh, &rl, &ll) < 4) {
359 fatalx(EXIT_FAILURE, "Insufficient parameters for runtimecal");
360 }
361
362 if ((rl < rh) || (rh <= 0)) {
363 fatalx(EXIT_FAILURE, "Parameter out of range (runtime)");
364 }
365
366 if ((lh > 100) || (ll > lh) || (ll <= 0)) {
367 fatalx(EXIT_FAILURE, "Parameter out of range (load)");
368 }
369
370 batt.runt.exp = log(rl / rh) / log(lh / ll);
371 upsdebugx(2, "%s: battery runtime exponent: %.3f", __func__, batt.runt.exp);
372
373 batt.runt.nom = rh * pow(lh / 100, batt.runt.exp);
374 upsdebugx(2, "%s: battery runtime nominal: %.1f", __func__, batt.runt.nom);
375
376 } else {
377
378 upslogx(LOG_INFO, "Battery runtime will not be calculated (runtimecal not set)");
379 return;
380
381 }
382
383 val = dstate_getinfo("battery.charge");
384 if (!val && (!d_equal(batt.volt.nom, -1))) {
385 batt.volt.low = batt.volt.nom;
386 batt.volt.high = 1.15 * batt.volt.nom;
387
388 if (qx_battery())
389 fatalx(EXIT_FAILURE, "Initial battery charge undetermined");
390
391 val = dstate_getinfo("battery.charge");
392 }
393
394 if (val) {
395 batt.runt.est = batt.runt.nom * strtod(val, NULL) / 100;
396 upsdebugx(2, "%s: battery runtime estimate: %.1f", __func__, batt.runt.est);
397 } else {
398 fatalx(EXIT_FAILURE, "Initial battery charge undetermined");
399 }
400
401 val = getval("chargetime");
402 if (val) {
403 batt.chrg.time = strtol(val, NULL, 10);
404
405 if (batt.chrg.time <= 0) {
406 fatalx(EXIT_FAILURE, "Charge time out of range [1..s]");
407 }
408
409 upsdebugx(2, "%s: battery charge time: %ld", __func__, batt.chrg.time);
410 } else {
411 upslogx(LOG_INFO, "No charge time specified, using built in default [%ld seconds]", batt.chrg.time);
412 }
413
414 val = getval("idleload");
415 if (val) {
416 load.low = strtod(val, NULL) / 100;
417
418 if ((load.low <= 0) || (load.low > 1)) {
419 fatalx(EXIT_FAILURE, "Idle load out of range [0..100]");
420 }
421
422 upsdebugx(2, "%s: minimum load used (idle): %.3f", __func__, load.low);
423 } else {
424 upslogx(LOG_INFO, "No idle load specified, using built in default [%.1f %%]", 100 * load.low);
425 }
426 }
427 }
428
429
430 /* == USB communication subdrivers == */
431 #if defined(QX_USB) && !defined(TESTING)
432 static usb_communication_subdriver_t *usb = &usb_subdriver;
433 static usb_dev_handle *udev = NULL;
434 static USBDevice_t usbdevice;
435 static USBDeviceMatcher_t *reopen_matcher = NULL;
436 static USBDeviceMatcher_t *regex_matcher = NULL;
437 static int langid_fix = -1;
438
439 static int (*subdriver_command)(const char *cmd, char *buf, size_t buflen) = NULL;
440
441 /* Cypress communication subdriver */
cypress_command(const char * cmd,char * buf,size_t buflen)442 static int cypress_command(const char *cmd, char *buf, size_t buflen)
443 {
444 char tmp[SMALLBUF];
445 int ret = 0;
446 size_t i;
447
448 if (buflen > INT_MAX) {
449 upsdebugx(3, "%s: requested to read too much (%zu), reducing buflen to (INT_MAX-1)",
450 __func__, buflen);
451 buflen = (INT_MAX - 1);
452 }
453
454 /* Send command */
455 memset(tmp, 0, sizeof(tmp));
456 snprintf(tmp, sizeof(tmp), "%s", cmd);
457
458 for (i = 0; i < strlen(tmp); i += (size_t)ret) {
459
460 /* Write data in 8-byte chunks */
461 /* ret = usb->set_report(udev, 0, (unsigned char *)&tmp[i], 8); */
462 ret = usb_control_msg(udev, USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE, 0x09, 0x200, 0, &tmp[i], 8, 5000);
463
464 if (ret <= 0) {
465 upsdebugx(3, "send: %s (%d)", ret ? usb_strerror() : "timeout", ret);
466 return ret;
467 }
468
469 }
470
471 upsdebugx(3, "send: %.*s", (int)strcspn(tmp, "\r"), tmp);
472
473 /* Read reply */
474 memset(buf, 0, buflen);
475
476 for (i = 0; (i <= buflen-8) && (memchr(buf, '\r', buflen) == NULL); i += (size_t)ret) {
477
478 /* Read data in 8-byte chunks */
479 /* ret = usb->get_interrupt(udev, (unsigned char *)&buf[i], 8, 1000); */
480 ret = usb_interrupt_read(udev, 0x81, &buf[i], 8, 1000);
481
482 /* Any errors here mean that we are unable to read a reply (which will happen after successfully writing a command to the UPS) */
483 if (ret <= 0) {
484 upsdebugx(3, "read: %s (%d)", ret ? usb_strerror() : "timeout", ret);
485 return ret;
486 }
487
488 snprintf(tmp, sizeof(tmp), "read [% 3d]", (int)i);
489 upsdebug_hex(5, tmp, &buf[i], (size_t)ret);
490
491 }
492
493 upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf);
494
495 if (i > INT_MAX) {
496 upsdebugx(3, "%s: read too much (%zu)", __func__, i);
497 return -1;
498 }
499 return (int)i;
500 }
501
502 /* SGS communication subdriver */
sgs_command(const char * cmd,char * buf,size_t buflen)503 static int sgs_command(const char *cmd, char *buf, size_t buflen)
504 {
505 char tmp[SMALLBUF];
506 int ret = 0;
507 size_t cmdlen, i;
508
509 if (buflen > INT_MAX) {
510 upsdebugx(3, "%s: requested to read too much (%zu), reducing buflen to (INT_MAX-1)",
511 __func__, buflen);
512 buflen = (INT_MAX - 1);
513 }
514
515 /* Send command */
516 cmdlen = strlen(cmd);
517
518 for (i = 0; i < cmdlen; i += (size_t)ret) {
519
520 memset(tmp, 0, sizeof(tmp));
521
522 /* i and cmdlen are size_t nominally, but diff is not large */
523 ret = (int)((cmdlen - i) < 7 ? (cmdlen - i) : 7);
524
525 /* ret is between 0 and 7 */
526 tmp[0] = (char)ret;
527 memcpy(&tmp[1], &cmd[i], (unsigned char)ret);
528
529 /* Write data in 8-byte chunks */
530 ret = usb_control_msg(udev, USB_ENDPOINT_OUT | USB_TYPE_CLASS | USB_RECIP_INTERFACE, 0x09, 0x200, 0, tmp, 8, 5000);
531
532 if (ret <= 0) {
533 upsdebugx(3, "send: %s (%d)", ret ? usb_strerror() : "timeout", ret);
534 return ret;
535 }
536
537 ret--;
538
539 }
540
541 upsdebugx(3, "send: %.*s", (int)strcspn(cmd, "\r"), cmd);
542
543 /* Read reply */
544 memset(buf, 0, buflen);
545
546 for (i = 0; i <= buflen - 8; i += (size_t)ret) {
547
548 memset(tmp, 0, sizeof(tmp));
549
550 /* Read data in 8-byte chunks */
551 ret = usb_interrupt_read(udev, 0x81, tmp, 8, 1000);
552
553 /* No error!!! */
554 /* TODO: Macro code */
555 if (ret == -110)
556 break;
557
558 /* Any errors here mean that we are unable to read a reply (which will happen after successfully writing a command to the UPS) */
559 if (ret <= 0) {
560 upsdebugx(3, "read: %s (%d)", ret ? usb_strerror() : "timeout", ret);
561 return ret;
562 }
563
564 /* Every call to read returns 8 bytes
565 * -> actually returned bytes: */
566 ret = tmp[0] <= 7 ? tmp[0] : 7;
567
568 if (ret > 0)
569 memcpy(&buf[i], &tmp[1], (unsigned char)ret);
570
571 snprintf(tmp, sizeof(tmp), "read [% 3d]", (int)i);
572 upsdebug_hex(5, tmp, &buf[i], (size_t)ret);
573
574 }
575
576 upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf);
577
578 if (i > INT_MAX) {
579 upsdebugx(3, "%s: read too much (%zu)", __func__, i);
580 return -1;
581 }
582 return (int)i;
583 }
584
585 /* Phoenix communication subdriver */
phoenix_command(const char * cmd,char * buf,size_t buflen)586 static int phoenix_command(const char *cmd, char *buf, size_t buflen)
587 {
588 char tmp[SMALLBUF];
589 int ret;
590 size_t i;
591
592 if (buflen > INT_MAX) {
593 upsdebugx(3, "%s: requested to read too much (%zu), reducing buflen to (INT_MAX-1)",
594 __func__, buflen);
595 buflen = (INT_MAX - 1);
596 }
597
598 for (i = 0; i < 8; i++) {
599
600 /* Read data in 8-byte chunks */
601 /* ret = usb->get_interrupt(udev, (unsigned char *)tmp, 8, 1000); */
602 ret = usb_interrupt_read(udev, 0x81, tmp, 8, 1000);
603
604 /* This USB to serial implementation is crappy.
605 * 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). */
606 switch (ret)
607 {
608 case -EPIPE: /* Broken pipe */
609 usb_clear_halt(udev, 0x81);
610 case -ETIMEDOUT: /* Connection timed out */
611 break;
612 }
613
614 if (ret < 0) {
615 upsdebugx(3, "flush: %s (%d)", usb_strerror(), ret);
616 break;
617 }
618
619 upsdebug_hex(4, "dump", tmp, (size_t)ret);
620
621 }
622
623 /* Send command */
624 memset(tmp, 0, sizeof(tmp));
625 snprintf(tmp, sizeof(tmp), "%s", cmd);
626
627 for (i = 0; i < strlen(tmp); i += (size_t)ret) {
628
629 /* Write data in 8-byte chunks */
630 /* ret = usb->set_report(udev, 0, (unsigned char *)&tmp[i], 8); */
631 ret = usb_control_msg(udev,
632 USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE,
633 0x09, 0x200, 0, &tmp[i], 8, 1000);
634
635 if (ret <= 0) {
636 upsdebugx(3, "send: %s (%d)",
637 ret ? usb_strerror() : "timeout", ret);
638 return ret;
639 }
640
641 }
642
643 upsdebugx(3, "send: %.*s", (int)strcspn(tmp, "\r"), tmp);
644
645 /* Read reply */
646 memset(buf, 0, buflen);
647
648 for (i = 0; (i <= buflen-8) && (memchr(buf, '\r', buflen) == NULL); i += (size_t)ret) {
649
650 /* Read data in 8-byte chunks */
651 /* ret = usb->get_interrupt(udev, (unsigned char *)&buf[i], 8, 1000); */
652 ret = usb_interrupt_read(udev, 0x81, &buf[i], 8, 1000);
653
654 /* Any errors here mean that we are unable to read a reply
655 * (which will happen after successfully writing a command
656 * to the UPS) */
657 if (ret <= 0) {
658 upsdebugx(3, "read: %s (%d)", ret ? usb_strerror() : "timeout", ret);
659 return ret;
660 }
661
662 snprintf(tmp, sizeof(tmp), "read [% 3d]", (int)i);
663 upsdebug_hex(5, tmp, &buf[i], (size_t)ret);
664
665 }
666
667 upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf);
668
669 if (i > INT_MAX) {
670 upsdebugx(3, "%s: read too much (%zu)", __func__, i);
671 return -1;
672 }
673 return (int)i;
674 }
675
676 /* Ippon communication subdriver */
ippon_command(const char * cmd,char * buf,size_t buflen)677 static int ippon_command(const char *cmd, char *buf, size_t buflen)
678 {
679 char tmp[64];
680 int ret;
681 size_t i, len;
682
683 if (buflen > INT_MAX) {
684 upsdebugx(3, "%s: requested to read too much (%zu), reducing buflen to (INT_MAX-1)",
685 __func__, buflen);
686 buflen = (INT_MAX - 1);
687 }
688
689 /* Send command */
690 snprintf(tmp, sizeof(tmp), "%s", cmd);
691
692 for (i = 0; i < strlen(tmp); i += (size_t)ret) {
693
694 /* Write data in 8-byte chunks */
695 ret = usb_control_msg(udev,
696 USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE,
697 0x09, 0x2, 0, &tmp[i], 8, 1000);
698
699 if (ret <= 0) {
700 upsdebugx(3, "send: %s (%d)",
701 (ret != -ETIMEDOUT) ? usb_strerror() : "Connection timed out",
702 ret);
703 return ret;
704 }
705
706 }
707
708 upsdebugx(3, "send: %.*s", (int)strcspn(tmp, "\r"), tmp);
709
710 /* Read all 64 bytes of the reply in one large chunk */
711 ret = usb_interrupt_read(udev, 0x81, tmp, sizeof(tmp), 1000);
712
713 /* Any errors here mean that we are unable to read a reply
714 * (which will happen after successfully writing a command
715 * to the UPS) */
716 if (ret <= 0) {
717 upsdebugx(3, "read: %s (%d)",
718 (ret != -ETIMEDOUT) ? usb_strerror() : "Connection timed out",
719 ret);
720 return ret;
721 }
722
723 /* As Ippon will always return 64 bytes in response,
724 * we have to calculate and return length of actual
725 * response data here.
726 * Empty response will look like 0x00 0x0D, otherwise
727 * it will be data string terminated by 0x0D. */
728
729 for (i = 0, len = 0; i < (size_t)ret; i++) {
730
731 if (tmp[i] != '\r')
732 continue;
733
734 len = ++i;
735 break;
736
737 }
738
739 /* Just in case there wasn't any '\r', fallback to string length, if any */
740 if (!len)
741 len = strlen(tmp);
742
743 upsdebug_hex(5, "read", tmp, (size_t)len);
744 upsdebugx(3, "read: %.*s", (int)strcspn(tmp, "\r"), tmp);
745
746 len = len < buflen ? len : buflen - 1;
747
748 memset(buf, 0, buflen);
749 memcpy(buf, tmp, len);
750
751 if (len > INT_MAX) {
752 upsdebugx(3, "%s: read too much (%zu)", __func__, len);
753 return -1;
754 }
755 return (int)len;
756 }
757
hunnox_protocol(int asking_for)758 static int hunnox_protocol(int asking_for)
759 {
760 char buf[1030];
761
762 int langid_fix_local = 0x0409;
763
764 if (langid_fix != -1) {
765 langid_fix_local = langid_fix;
766 }
767
768 switch (hunnox_step) {
769 case 0:
770 upsdebugx(3, "asking for: %02X", 0x00);
771 usb_get_string(udev, 0x00, langid_fix_local, buf, 1026);
772 usb_get_string(udev, 0x00, langid_fix_local, buf, 1026);
773 usb_get_string(udev, 0x01, langid_fix_local, buf, 1026);
774 usleep(10000);
775 break;
776 case 1:
777 if (asking_for != 0x0d) {
778 upsdebugx(3, "asking for: %02X", 0x0d);
779 usb_get_string(udev, 0x0d, langid_fix_local, buf, 102);
780 }
781 break;
782 case 2:
783 if (asking_for != 0x03) {
784 upsdebugx(3, "asking for: %02X", 0x03);
785 usb_get_string(udev, 0x03, langid_fix_local, buf, 102);
786 }
787 break;
788 case 3:
789 if (asking_for != 0x0c) {
790 upsdebugx(3, "asking for: %02X", 0x0c);
791 usb_get_string(udev, 0x0c, langid_fix_local, buf, 102);
792 }
793 break;
794 default:
795 hunnox_step = 0;
796 }
797 hunnox_step++;
798 if (hunnox_step > 3) {
799 hunnox_step = 1;
800 }
801
802 return 0;
803 }
804
805 /* Krauler communication subdriver */
krauler_command(const char * cmd,char * buf,size_t buflen)806 static int krauler_command(const char *cmd, char *buf, size_t buflen)
807 {
808 /* Still not implemented:
809 * 0x6 T<n> (don't know how to pass the parameter)
810 * 0x68 and 0x69 both cause shutdown after an undefined interval */
811 const struct {
812 const char *str; /* Megatec command */
813 const int index; /* Krauler string index for this command */
814 const char prefix; /* Character to replace the first byte in reply */
815 } command[] = {
816 { "Q1\r", 0x03, '(' },
817 { "F\r", 0x0d, '#' },
818 { "I\r", 0x0c, '#' },
819 { "T\r", 0x04, '\r' },
820 { "TL\r", 0x05, '\r' },
821 { "Q\r", 0x07, '\r' },
822 { "C\r", 0x0b, '\r' },
823 { "CT\r", 0x0b, '\r' },
824 { NULL, 0, '\0' }
825 };
826
827 int i;
828
829 upsdebugx(3, "send: %.*s", (int)strcspn(cmd, "\r"), cmd);
830
831 if (buflen > INT_MAX) {
832 upsdebugx(3, "%s: requested to read too much (%zu), reducing buflen to (INT_MAX-1)",
833 __func__, buflen);
834 buflen = (INT_MAX - 1);
835 }
836
837 for (i = 0; command[i].str; i++) {
838
839 int retry;
840
841 if (strcmp(cmd, command[i].str)) {
842 continue;
843 }
844
845 for (retry = 0; retry < 10; retry++) {
846
847 int ret;
848
849 if (langid_fix != -1) {
850 /* Apply langid_fix value */
851 ret = usb_get_string(udev, command[i].index, langid_fix, buf, buflen);
852 } else {
853 ret = usb_get_string_simple(udev, command[i].index, buf, buflen);
854 }
855
856 if (ret <= 0) {
857 upsdebugx(3, "read: %s (%d)",
858 ret ? usb_strerror() : "timeout", ret);
859 return ret;
860 }
861
862 /* This may serve in the future */
863 upsdebugx(1, "received %d (%d)", ret, buf[0]);
864
865 if (langid_fix != -1) {
866 /* Limit this check, at least for now */
867 /* Invalid receive size - message corrupted */
868 if (ret != buf[0]) {
869 upsdebugx(1, "size mismatch: %d / %d", ret, buf[0]);
870 continue;
871 }
872
873 /* Simple unicode -> ASCII inplace conversion
874 * FIXME: this code is at least shared with mge-shut/libshut
875 * Create a common function? */
876 unsigned int di, si, size = (unsigned int)buf[0];
877 for (di = 0, si = 2; si < size; si += 2) {
878
879 if (di >= (buflen - 1))
880 break;
881
882 if (buf[si + 1]) /* high byte */
883 buf[di++] = '?';
884 else
885 buf[di++] = buf[si];
886
887 }
888
889 /* Note: effective range of di should be unsigned char */
890 buf[di] = 0;
891 ret = (int)di;
892 }
893
894 /* "UPS No Ack" has a special meaning */
895 if (
896 strcspn(buf, "\r") == 10 &&
897 !strncasecmp(buf, "UPS No Ack", 10)
898 ) {
899 upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf);
900 continue;
901 }
902
903 /* Replace the first byte of what we received with the correct one */
904 buf[0] = command[i].prefix;
905
906 upsdebug_hex(5, "read", buf, (size_t)ret);
907 upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf);
908
909 return ret;
910
911 }
912
913 return 0;
914
915 }
916
917 /* Echo the unknown command back */
918 upsdebugx(3, "read: %.*s", (int)strcspn(cmd, "\r"), cmd);
919 return snprintf(buf, buflen, "%s", cmd);
920 }
921
922 /* Fabula communication subdriver */
fabula_command(const char * cmd,char * buf,size_t buflen)923 static int fabula_command(const char *cmd, char *buf, size_t buflen)
924 {
925 const struct {
926 const char *str; /* Megatec command */
927 const int index; /* Fabula string index for this command */
928 } commands[] = {
929 { "Q1\r", 0x03, }, /* Status */
930 { "F\r", 0x0d, }, /* Ratings */
931 { "I\r", 0x0c, }, /* Vendor infos */
932 { "Q\r", 0x07, }, /* Beeper toggle */
933 { "C\r", 0x0a, }, /* Cancel shutdown/Load on [0x(0..F)A]*/
934 { NULL, 0 }
935 };
936 int i, ret, index = 0;
937
938 upsdebugx(3, "send: %.*s", (int)strcspn(cmd, "\r"), cmd);
939
940 if (buflen > INT_MAX) {
941 upsdebugx(3, "%s: requested to read too much (%zu), reducing buflen to (INT_MAX-1)",
942 __func__, buflen);
943 buflen = (INT_MAX - 1);
944 }
945
946 for (i = 0; commands[i].str; i++) {
947
948 if (strcmp(cmd, commands[i].str))
949 continue;
950
951 index = commands[i].index;
952 break;
953
954 }
955
956 if (!index) {
957
958 int val2 = -1;
959 double val1 = -1;
960
961 /* Shutdowns */
962 if (
963 sscanf(cmd, "S%lfR%d\r", &val1, &val2) == 2 ||
964 sscanf(cmd, "S%lf\r", &val1) == 1
965 ) {
966
967 double delay;
968
969 /* 0x(1+)0 -> shutdown.stayoff (SnR0000)
970 * 0x(1+)8 -> shutdown.return (Sn[Rm], m != 0) [delay before restart is always 10 seconds]
971 * +0x10 (16dec) = next megatec delay (min .5 = hex 0x1*; max 10 = hex 0xF*) -> n < 1 ? -> n += .1; n >= 1 ? -> n += 1 */
972
973 /* delay: [.5..10] (-> seconds: [30..600]) */
974 delay = val1 < .5 ? .5 : val1 > 10 ? 10 : val1;
975
976 if (delay < 1)
977 index = 16 + round((delay - .5) * 10) * 16;
978 else
979 index = 96 + (delay - 1) * 16;
980
981 /* shutdown.return (Sn[Rm], m != 0) */
982 if (val2)
983 index += 8;
984
985 /* Unknown commands */
986 } else {
987
988 /* Echo the unknown command back */
989 upsdebugx(3, "read: %.*s", (int)strcspn(cmd, "\r"), cmd);
990 return snprintf(buf, buflen, "%s", cmd);
991
992 }
993
994 }
995
996 upsdebugx(4, "command index: 0x%02x", index);
997
998 /* Send command/Read reply */
999 ret = usb_get_string_simple(udev, index, buf, buflen);
1000
1001 if (ret <= 0) {
1002 upsdebugx(3, "read: %s (%d)", ret ? usb_strerror() : "timeout", ret);
1003 return ret;
1004 }
1005
1006 upsdebug_hex(5, "read", buf, (size_t)ret);
1007 upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf);
1008
1009 /* The UPS always replies "UPS No Ack" when a supported command is issued (either if it fails or if it succeeds).. */
1010 if (
1011 strcspn(buf, "\r") == 10 &&
1012 !strncasecmp(buf, "UPS No Ack", 10)
1013 ) {
1014 /* ..because of that, always return 0 (with buf empty, as if it was a timeout): queries will see it as a failure, instant commands ('megatec' protocol) as a success */
1015 memset(buf, 0, buflen);
1016 return 0;
1017 }
1018
1019 return ret;
1020 }
1021
1022 /* Hunnox communication subdriver, based on Fabula code above so repeats
1023 * much of it currently. Possible future optimization is to refactor shared
1024 * code into new routines to be called from both (or more) methods.*/
hunnox_command(const char * cmd,char * buf,size_t buflen)1025 static int hunnox_command(const char *cmd, char *buf, size_t buflen)
1026 {
1027 /* The hunnox_patch was an argument in initial implementation of PR #638
1028 * which added "hunnox" support; keeping it fixed here helps to visibly
1029 * track the modifications compared to original fabula_command() e.g. to
1030 * facilitate refactoring commented above, in the future.
1031 */
1032 /* char hunnox_patch = 1; */
1033 const struct {
1034 const char *str; /* Megatec command */
1035 const int index; /* Fabula string index for this command */
1036 } commands[] = {
1037 { "Q1\r", 0x03, }, /* Status */
1038 { "F\r", 0x0d, }, /* Ratings */
1039 { "I\r", 0x0c, }, /* Vendor infos */
1040 { "Q\r", 0x07, }, /* Beeper toggle */
1041 { "C\r", 0x0a, }, /* Cancel shutdown/Load on [0x(0..F)A]*/
1042 { NULL, 0 }
1043 };
1044 int i, ret, index = 0;
1045
1046 upsdebugx(3, "send: %.*s", (int)strcspn(cmd, "\r"), cmd);
1047
1048 if (buflen > INT_MAX) {
1049 upsdebugx(3, "%s: requested to read too much (%zu), reducing buflen to (INT_MAX-1)",
1050 __func__, buflen);
1051 buflen = (INT_MAX - 1);
1052 }
1053
1054 for (i = 0; commands[i].str; i++) {
1055
1056 if (strcmp(cmd, commands[i].str))
1057 continue;
1058
1059 index = commands[i].index;
1060 break;
1061
1062 }
1063
1064 if (!index) {
1065
1066 int val2 = -1;
1067 double val1 = -1;
1068
1069 /* Shutdowns */
1070 if (
1071 sscanf(cmd, "S%lfR%d\r", &val1, &val2) == 2 ||
1072 sscanf(cmd, "S%lf\r", &val1) == 1
1073 ) {
1074
1075 double delay;
1076
1077 /* 0x(1+)0 -> shutdown.stayoff (SnR0000)
1078 * 0x(1+)8 -> shutdown.return (Sn[Rm], m != 0) [delay before restart is always 10 seconds]
1079 * +0x10 (16dec) = next megatec delay (min .5 = hex 0x1*; max 10 = hex 0xF*) -> n < 1 ? -> n += .1; n >= 1 ? -> n += 1 */
1080
1081 /* delay: [.5..10] (-> seconds: [30..600]) */
1082 delay = val1 < .5 ? .5 : val1 > 10 ? 10 : val1;
1083
1084 if (delay < 1)
1085 index = 16 + round((delay - .5) * 10) * 16;
1086 else
1087 index = 96 + (delay - 1) * 16;
1088
1089 /* shutdown.return (Sn[Rm], m != 0) */
1090 if (val2)
1091 index += 8;
1092
1093 /* Unknown commands */
1094 } else {
1095
1096 /* Echo the unknown command back */
1097 upsdebugx(3, "read: %.*s", (int)strcspn(cmd, "\r"), cmd);
1098 return snprintf(buf, buflen, "%s", cmd);
1099
1100 }
1101
1102 }
1103
1104 upsdebugx(4, "command index: 0x%02x", index);
1105
1106 /* if (hunnox_patch) { */
1107 // Enable lock-step protocol for Hunnox
1108 if (hunnox_protocol(index) != 0) {
1109 return 0;
1110 }
1111
1112 // Seems that if we inform a large buffer, the USB locks.
1113 // This value was captured from the Windows "official" client.
1114 // Note this should not be a problem programmatically: it just
1115 // means that the caller reserved a longer buffer that we need
1116 // in practice to write a response into.
1117 if (buflen > 102) {
1118 buflen = 102;
1119 }
1120 /* } */
1121
1122 /* Send command/Read reply */
1123 if (langid_fix != -1) {
1124 ret = usb_get_string(udev, index, langid_fix, buf, buflen);
1125 } else {
1126 ret = usb_get_string_simple(udev, index, buf, buflen);
1127 }
1128
1129 if (ret <= 0) {
1130 upsdebugx(3, "read: %s (%d)", ret ? usb_strerror() : "timeout", ret);
1131 return ret;
1132 }
1133
1134 /* if (hunnox_patch) { */
1135 if (langid_fix != -1) {
1136 /* Limit this check, at least for now */
1137 /* Invalid receive size - message corrupted */
1138 if (ret != buf[0]) {
1139 upsdebugx(1, "size mismatch: %d / %d", ret, buf[0]);
1140 return 0;
1141 }
1142
1143 /* Simple unicode -> ASCII inplace conversion
1144 * FIXME: this code is at least shared with mge-shut/libshut
1145 * Create a common function? */
1146 unsigned int di, si, size = (unsigned int)buf[0];
1147 for (di = 0, si = 2; si < size; si += 2) {
1148 if (di >= (buflen - 1))
1149 break;
1150
1151 if (buf[si + 1]) /* high byte */
1152 buf[di++] = '?';
1153 else
1154 buf[di++] = buf[si];
1155 }
1156
1157 /* Note: effective range of di should be unsigned char */
1158 buf[di] = 0;
1159 ret = (int)di;
1160 }
1161 /* } */
1162
1163 upsdebug_hex(5, "read", buf, (size_t)ret);
1164 upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf);
1165
1166 /* The UPS always replies "UPS No Ack" when a supported command is issued (either if it fails or if it succeeds).. */
1167 if (
1168 strcspn(buf, "\r") == 10 &&
1169 !strncasecmp(buf, "UPS No Ack", 10)
1170 ) {
1171 /* ..because of that, always return 0 (with buf empty, as if it was a timeout): queries will see it as a failure, instant commands ('megatec' protocol) as a success */
1172 memset(buf, 0, buflen);
1173 return 0;
1174 }
1175
1176 return ret;
1177 }
1178
1179 /* Fuji communication subdriver */
fuji_command(const char * cmd,char * buf,size_t buflen)1180 static int fuji_command(const char *cmd, char *buf, size_t buflen)
1181 {
1182 unsigned char tmp[8];
1183 char command[SMALLBUF] = "",
1184 read[SMALLBUF] = "";
1185 int ret, val2;
1186 unsigned char answer_len;
1187 double val1;
1188 size_t i;
1189 const struct {
1190 const char *command; /* Megatec command */
1191 const unsigned char answer_len; /* Expected length of the answer to the ongoing query */
1192 } query[] = {
1193 { "Q1", 47 },
1194 { "F", 22 },
1195 { "I", 39 },
1196 { NULL, 0 }
1197 };
1198
1199 if (buflen > INT_MAX) {
1200 upsdebugx(3, "%s: requested to read too much (%zu), reducing buflen to (INT_MAX-1)",
1201 __func__, buflen);
1202 buflen = (INT_MAX - 1);
1203 }
1204
1205 /*
1206 * Queries (b1..b8) sent (as a 8-bytes interrupt) to the UPS adopt the following scheme:
1207 *
1208 * b1: 0x80
1209 * b2: 0x06
1210 * b3: <LEN>
1211 * b4: 0x03
1212 * b5..bn: <COMMAND>
1213 * bn+1..b7: [<PADDING>]
1214 * b8: <ANSWER_LEN>
1215 *
1216 * Where:
1217 * <LEN> Length (in Hex) of the command (without the trailing CR) + 1
1218 * <COMMAND> Command/query (without the trailing CR)
1219 * [<PADDING>] 0x00 padding to the 7th byte
1220 * <ANSWER_LEN> Expected length (in Hex) of the answer to the ongoing query (0 when no reply is expected, i.e. commands)
1221 *
1222 * Replies to queries (commands are followed by action without any reply) are sent from the UPS (in 8-byte chunks) with 0x00 padding after the trailing CR to full 8 bytes.
1223 *
1224 */
1225
1226 /* Send command */
1227
1228 /* Remove the CR */
1229 snprintf(command, sizeof(command), "%.*s", (int)strcspn(cmd, "\r"), cmd);
1230
1231 /* Length of the command that will be sent to the UPS can be at most: 8 - 5 (0x80, 0x06, <LEN>, 0x03, <ANSWER_LEN>) = 3.
1232 * As a consequence also 'SnRm' commands (shutdown.{return,stayoff} and load.off) are not supported.
1233 * So, map all the 'SnRm' shutdown.returns (m != 0) as the corresponding 'Sn' commands, meanwhile ignoring ups.delay.start and making the UPS turn on the load as soon as power is back. */
1234 if (sscanf(cmd, "S%lfR%d\r", &val1, &val2) == 2 && val2) {
1235 upsdebugx(4, "%s: trimming '%s' to '%.*s'", __func__, command, 3, command);
1236 command[3] = 0;
1237 }
1238 /* Too long command */
1239 if (strlen(command) > 3) {
1240 /* Be 'megatec-y': echo the unsupported command back */
1241 upsdebugx(3, "%s: unsupported command %s", __func__, command);
1242 return snprintf(buf, buflen, "%s", cmd);
1243 }
1244
1245 /* Expected length of the answer to the ongoing query (0 when no reply is expected, i.e. commands) */
1246 answer_len = 0;
1247 for (i = 0; query[i].command; i++) {
1248
1249 if (strcmp(command, query[i].command))
1250 continue;
1251
1252 answer_len = query[i].answer_len;
1253 break;
1254
1255 }
1256
1257 memset(tmp, 0, sizeof(tmp));
1258
1259 /* 0x80 */
1260 tmp[0] = 0x80;
1261 /* 0x06 */
1262 tmp[1] = 0x06;
1263 /* <LEN>; per above under 3 */
1264 tmp[2] = (unsigned char)strlen(command) + 1;
1265 /* 0x03 */
1266 tmp[3] = 0x03;
1267 /* <COMMAND> */
1268 memcpy(&tmp[4], command, strlen(command));
1269 /* <ANSWER_LEN> */
1270 tmp[7] = answer_len;
1271
1272 upsdebug_hex(4, "command", (char *)tmp, 8);
1273
1274 /* Write data */
1275 ret = usb_interrupt_write(udev, USB_ENDPOINT_OUT | 2, (char *)tmp, 8, USB_TIMEOUT);
1276
1277 if (ret <= 0) {
1278 upsdebugx(3, "send: %s (%d)", ret ? usb_strerror() : "timeout", ret);
1279 return ret;
1280 }
1281
1282 upsdebugx(3, "send: %s", command);
1283
1284 /* Read reply */
1285
1286 memset(buf, 0, buflen);
1287
1288 for (i = 0; (i <= buflen - 8) && (memchr(buf, '\r', buflen) == NULL); i += (size_t)ret) {
1289
1290 /* Read data in 8-byte chunks */
1291 ret = usb_interrupt_read(udev, USB_ENDPOINT_IN | 1, &buf[i], 8, 1000);
1292
1293 /* Any errors here mean that we are unable to read a reply (which will happen after successfully writing a command to the UPS) */
1294 if (ret <= 0) {
1295 upsdebugx(3, "read: %s (%d)", ret ? usb_strerror() : "timeout", ret);
1296 return ret;
1297 }
1298
1299 snprintf(read, sizeof(read), "read [%3d]", (int)i);
1300 upsdebug_hex(5, read, &buf[i], (size_t)ret);
1301
1302 }
1303
1304 upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf);
1305
1306 /* As Fuji units return the reply in 8-byte chunks always padded to the 8th byte with 0x00, we need to calculate and return the length of the actual response here. */
1307 return (int)strlen(buf);
1308 }
1309
1310 /* Phoenixtec (Masterguard) communication subdriver */
phoenixtec_command(const char * cmd,char * buf,size_t buflen)1311 static int phoenixtec_command(const char *cmd, char *buf, size_t buflen)
1312 {
1313 int ret;
1314 char *p, *e = NULL;
1315 char *l[] = { "T", "TL", "S", "C", "CT", "M", "N", "O", "SRC", "FCLR", "SS", "TUD", "SSN", NULL }; /* commands that don't return an answer */
1316 char **lp;
1317 size_t cmdlen = strlen(cmd);
1318
1319 if (cmdlen > INT_MAX) {
1320 upsdebugx(3, "%s: requested command is too long (%zu)",
1321 __func__, cmdlen);
1322 return 0;
1323 }
1324
1325 if (buflen > INT_MAX) {
1326 upsdebugx(3, "%s: requested to read too much (%zu), reducing buflen to (INT_MAX-1)",
1327 __func__, buflen);
1328 buflen = (INT_MAX - 1);
1329 }
1330
1331 if ((ret = usb_control_msg(udev,
1332 USB_ENDPOINT_OUT | USB_TYPE_VENDOR | USB_RECIP_ENDPOINT,
1333 0x0d, 0, 0, (char *)cmd, (int)cmdlen, 1000)) <= 0
1334 ) {
1335 upsdebugx(3, "send: %s (%d)", ret ? usb_strerror() : "timeout", ret);
1336 *buf = '\0';
1337 return ret;
1338 }
1339
1340 for (lp = l; *lp != NULL; lp++) {
1341 const char *q;
1342 int b;
1343
1344 p = *lp; q = cmd; b = 1;
1345 while (*p != '\0') {
1346 if (*p++ != *q++) {
1347 b = 0;
1348 break;
1349 }
1350 }
1351 if (b && *q >= 'A' && *q <= 'Z') b = 0; /* "M" not to match "MSO" */
1352 if (b) {
1353 upsdebugx(4, "command %s returns no answer", *lp);
1354 *buf = '\0';
1355 return 0;
1356 }
1357 }
1358
1359 for (p = buf; p < buf + buflen; p += ret) {
1360 /* buflen constrained to INT_MAX above, so we can cast: */
1361 if ((ret = usb_interrupt_read(udev,
1362 USB_ENDPOINT_IN | 1,
1363 p, (int)(buf + buflen - p), 1000)) <= 0
1364 ) {
1365 upsdebugx(3, "read: %s (%d)", ret ? usb_strerror() : "timeout", ret);
1366 *buf = '\0';
1367 return ret;
1368 }
1369 if ((e = memchr(p, '\r', (size_t)ret)) != NULL) break;
1370 }
1371 if (e != NULL && ++e < buf + buflen) {
1372 *e = '\0';
1373 /* buflen constrained to INT_MAX above, so we can cast: */
1374 return (int)(e - buf);
1375 } else {
1376 upsdebugx(3, "read: buflen %zu too small", buflen);
1377 *buf = '\0';
1378 return 0;
1379 }
1380 }
1381
1382 /* SNR communication subdriver */
snr_command(const char * cmd,char * buf,size_t buflen)1383 static int snr_command(const char *cmd, char *buf, size_t buflen)
1384 {
1385 /*ATTENTION: This subdriver uses short buffer with length 102 byte*/
1386 const struct {
1387 const char *str; /* Megatec command */
1388 const int index; /* String index for this command */
1389 const char prefix; /* Character to replace the first byte in reply */
1390 } command[] = {
1391 { "Q1\r", 0x03, '(' },
1392 { "F\r", 0x0d, '#' },
1393 { "I\r", 0x0c, '#' },
1394 { NULL, 0, '\0' }
1395 };
1396
1397 int i;
1398
1399 upsdebugx(3, "send: %.*s", (int)strcspn(cmd, "\r"), cmd);
1400
1401 if (buflen > INT_MAX) {
1402 upsdebugx(3, "%s: requested to read too much (%zu), reducing buflen to (INT_MAX-1)",
1403 __func__, buflen);
1404 buflen = (INT_MAX - 1);
1405 }
1406
1407 if (buflen < 102) {
1408 upsdebugx(4, "size of buf less than 102 byte!");
1409 return 0;
1410 }
1411
1412 for (i = 0; command[i].str; i++) {
1413
1414 int retry;
1415
1416 if (strcmp(cmd, command[i].str)) {
1417 continue;
1418 }
1419
1420 for (retry = 0; retry < 10; retry++) {
1421
1422 int ret;
1423
1424 ret = usb_get_string(udev, command[i].index, langid_fix, buf, 102);
1425
1426 if (ret <= 0) {
1427 upsdebugx(3, "read: %s (%d)", ret ? usb_strerror() : "timeout", ret);
1428 return ret;
1429 }
1430
1431 /* This may serve in the future */
1432 upsdebugx(1, "received %d (%d)", ret, buf[0]);
1433
1434
1435 if (ret != buf[0]) {
1436 upsdebugx(1, "size mismatch: %d / %d", ret, buf[0]);
1437 continue;
1438 }
1439
1440 /* Simple unicode -> ASCII inplace conversion
1441 * FIXME: this code is at least shared with mge-shut/libshut
1442 * Create a common function? */
1443 unsigned int di, si, size = (unsigned int)buf[0];
1444 for (di = 0, si = 2; si < size; si += 2) {
1445
1446 if (di >= (buflen - 1))
1447 break;
1448
1449 if (buf[si + 1]) /* high byte */
1450 buf[di++] = '?';
1451 else
1452 buf[di++] = buf[si];
1453
1454 }
1455
1456 /* Note: effective range of di should be unsigned char */
1457 buf[di] = 0;
1458 ret = (int)di;
1459
1460 /* "UPS No Ack" has a special meaning */
1461 if (
1462 strcspn(buf, "\r") == 10 &&
1463 !strncasecmp(buf, "UPS No Ack", 10)
1464 ) {
1465 upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf);
1466 continue;
1467 }
1468
1469 /* Replace the first byte of what we received with the correct one */
1470 buf[0] = command[i].prefix;
1471
1472 upsdebug_hex(5, "read", buf, (size_t)ret);
1473 upsdebugx(3, "read: %.*s", (int)strcspn(buf, "\r"), buf);
1474
1475 return ret;
1476
1477 }
1478
1479 return 0;
1480
1481 }
1482
1483 /* Echo the unknown command back */
1484 upsdebugx(3, "read: %.*s", (int)strcspn(cmd, "\r"), cmd);
1485 return snprintf(buf, buflen, "%s", cmd);
1486 }
1487
cypress_subdriver(USBDevice_t * device)1488 static void *cypress_subdriver(USBDevice_t *device)
1489 {
1490 NUT_UNUSED_VARIABLE(device);
1491
1492 subdriver_command = &cypress_command;
1493 return NULL;
1494 }
1495
sgs_subdriver(USBDevice_t * device)1496 static void *sgs_subdriver(USBDevice_t *device)
1497 {
1498 NUT_UNUSED_VARIABLE(device);
1499
1500 subdriver_command = &sgs_command;
1501 return NULL;
1502 }
1503
ippon_subdriver(USBDevice_t * device)1504 static void *ippon_subdriver(USBDevice_t *device)
1505 {
1506 NUT_UNUSED_VARIABLE(device);
1507
1508 subdriver_command = &ippon_command;
1509 return NULL;
1510 }
1511
krauler_subdriver(USBDevice_t * device)1512 static void *krauler_subdriver(USBDevice_t *device)
1513 {
1514 NUT_UNUSED_VARIABLE(device);
1515
1516 subdriver_command = &krauler_command;
1517 return NULL;
1518 }
1519
phoenix_subdriver(USBDevice_t * device)1520 static void *phoenix_subdriver(USBDevice_t *device)
1521 {
1522 NUT_UNUSED_VARIABLE(device);
1523
1524 subdriver_command = &phoenix_command;
1525 return NULL;
1526 }
1527
fabula_subdriver(USBDevice_t * device)1528 static void *fabula_subdriver(USBDevice_t *device)
1529 {
1530 NUT_UNUSED_VARIABLE(device);
1531
1532 subdriver_command = &fabula_command;
1533 return NULL;
1534 }
1535
phoenixtec_subdriver(USBDevice_t * device)1536 static void *phoenixtec_subdriver(USBDevice_t *device)
1537 {
1538 NUT_UNUSED_VARIABLE(device);
1539
1540 subdriver_command = &phoenixtec_command;
1541 return NULL;
1542 }
1543
1544 /* Note: the "hunnox_subdriver" name is taken by the subdriver_t structure */
fabula_hunnox_subdriver(USBDevice_t * device)1545 static void *fabula_hunnox_subdriver(USBDevice_t *device)
1546 {
1547 NUT_UNUSED_VARIABLE(device);
1548
1549 subdriver_command = &hunnox_command;
1550 return NULL;
1551 }
1552
fuji_subdriver(USBDevice_t * device)1553 static void *fuji_subdriver(USBDevice_t *device)
1554 {
1555 NUT_UNUSED_VARIABLE(device);
1556
1557 subdriver_command = &fuji_command;
1558 return NULL;
1559 }
1560
snr_subdriver(USBDevice_t * device)1561 static void *snr_subdriver(USBDevice_t *device)
1562 {
1563 NUT_UNUSED_VARIABLE(device);
1564
1565 subdriver_command = &snr_command;
1566 return NULL;
1567 }
1568
1569 /* USB device match structure */
1570 typedef struct {
1571 const int vendorID; /* USB device's VendorID */
1572 const int productID; /* USB device's ProductID */
1573 const char *vendor; /* USB device's iManufacturer string */
1574 const char *product; /* USB device's iProduct string */
1575 void *(*fun)(USBDevice_t *); /* Handler for specific processing */
1576 } qx_usb_device_id_t;
1577
1578 /* USB VendorID/ProductID/iManufacturer/iProduct match - note: rightmost comment is used for naming rules by tools/nut-usbinfo.pl */
1579 static qx_usb_device_id_t qx_usb_id[] = {
1580 { USB_DEVICE(0x05b8, 0x0000), NULL, NULL, &cypress_subdriver }, /* Agiler UPS */
1581 { USB_DEVICE(0xffff, 0x0000), NULL, NULL, &krauler_subdriver }, /* Ablerex 625L USB */
1582 { USB_DEVICE(0x0665, 0x5161), NULL, NULL, &cypress_subdriver }, /* Belkin F6C1200-UNV/Voltronic Power UPSes */
1583 { USB_DEVICE(0x06da, 0x0002), "Phoenixtec Power","USB Cable (V2.00)", &phoenixtec_subdriver },/* Masterguard A Series */
1584 { USB_DEVICE(0x06da, 0x0002), NULL, NULL, &cypress_subdriver }, /* Online Yunto YQ450 */
1585 { USB_DEVICE(0x06da, 0x0003), NULL, NULL, &ippon_subdriver }, /* Mustek Powermust */
1586 { USB_DEVICE(0x06da, 0x0004), NULL, NULL, &cypress_subdriver }, /* Phoenixtec Innova 3/1 T */
1587 { USB_DEVICE(0x06da, 0x0005), NULL, NULL, &cypress_subdriver }, /* Phoenixtec Innova RT */
1588 { USB_DEVICE(0x06da, 0x0201), NULL, NULL, &cypress_subdriver }, /* Phoenixtec Innova T */
1589 { USB_DEVICE(0x06da, 0x0601), NULL, NULL, &phoenix_subdriver }, /* Online Zinto A */
1590 { USB_DEVICE(0x0f03, 0x0001), NULL, NULL, &cypress_subdriver }, /* Unitek Alpha 1200Sx */
1591 { USB_DEVICE(0x14f0, 0x00c9), NULL, NULL, &phoenix_subdriver }, /* GE EP series */
1592 { USB_DEVICE(0x0483, 0x0035), NULL, NULL, &sgs_subdriver }, /* TS Shara UPSes; vendor ID 0x0483 is from ST Microelectronics - with product IDs delegated to different OEMs */
1593 { USB_DEVICE(0x0001, 0x0000), "MEC", "MEC0003", &fabula_subdriver }, /* Fideltronik/MEC LUPUS 500 USB */
1594 { USB_DEVICE(0x0001, 0x0000), NULL, "MEC0003", &fabula_hunnox_subdriver }, /* Hunnox HNX 850, reported to also help support Powercool and some other devices; closely related to fabula with tweaks */
1595 { USB_DEVICE(0x0001, 0x0000), "ATCL FOR UPS", "ATCL FOR UPS", &fuji_subdriver }, /* Fuji UPSes */
1596 { USB_DEVICE(0x0001, 0x0000), NULL, NULL, &krauler_subdriver }, /* Krauler UP-M500VA */
1597 { USB_DEVICE(0x0001, 0x0000), NULL, "MEC0003", &snr_subdriver }, /* SNR-UPS-LID-XXXX UPSes */
1598 /* End of list */
1599 { -1, -1, NULL, NULL, NULL }
1600 };
1601
qx_is_usb_device_supported(qx_usb_device_id_t * usb_device_id_list,USBDevice_t * device)1602 static int qx_is_usb_device_supported(qx_usb_device_id_t *usb_device_id_list, USBDevice_t *device)
1603 {
1604 int retval = NOT_SUPPORTED;
1605 qx_usb_device_id_t *usbdev;
1606
1607 for (usbdev = usb_device_id_list; usbdev->vendorID != -1; usbdev++) {
1608
1609 if (usbdev->vendorID != device->VendorID)
1610 continue;
1611
1612 /* Flag as possibly supported if we see a known vendor */
1613 retval = POSSIBLY_SUPPORTED;
1614
1615 if (usbdev->productID != device->ProductID)
1616 continue;
1617
1618 if (usbdev->vendor && (!device->Vendor || strcasecmp(usbdev->vendor, device->Vendor)))
1619 continue;
1620
1621 if (usbdev->product && (!device->Product || strcasecmp(usbdev->product, device->Product)))
1622 continue;
1623
1624 /* Call the specific handler, if it exists */
1625 if (usbdev->fun != NULL)
1626 (*usbdev->fun)(device);
1627
1628 return SUPPORTED;
1629
1630 }
1631
1632 return retval;
1633 }
1634
device_match_func(USBDevice_t * hd,void * privdata)1635 static int device_match_func(USBDevice_t *hd, void *privdata)
1636 {
1637 NUT_UNUSED_VARIABLE(privdata);
1638
1639 if (subdriver_command) {
1640 return 1;
1641 }
1642
1643 switch (qx_is_usb_device_supported(qx_usb_id, hd))
1644 {
1645 case SUPPORTED:
1646 return 1;
1647
1648 case POSSIBLY_SUPPORTED:
1649 case NOT_SUPPORTED:
1650 default:
1651 return 0;
1652 }
1653 }
1654
1655 static USBDeviceMatcher_t device_matcher = {
1656 &device_match_func,
1657 NULL,
1658 NULL
1659 };
1660 #endif /* QX_USB && !TESTING */
1661
1662
1663 /* == Driver functions implementations == */
1664
1665 /* See header file for details. */
instcmd(const char * cmdname,const char * extradata)1666 int instcmd(const char *cmdname, const char *extradata)
1667 {
1668 item_t *item;
1669 char value[SMALLBUF];
1670
1671 if (!strcasecmp(cmdname, "beeper.off")) {
1672 /* Compatibility mode for old command */
1673 upslogx(LOG_WARNING, "The 'beeper.off' command has been renamed to 'beeper.disable'");
1674 return instcmd("beeper.disable", NULL);
1675 }
1676
1677 if (!strcasecmp(cmdname, "beeper.on")) {
1678 /* Compatibility mode for old command */
1679 upslogx(LOG_WARNING, "The 'beeper.on' command has been renamed to 'beeper.enable'");
1680 return instcmd("beeper.enable", NULL);
1681 }
1682
1683 upslogx(LOG_INFO, "%s(%s, %s)", __func__, cmdname, extradata ? extradata : "[NULL]");
1684
1685 /* Retrieve item by command name */
1686 item = find_nut_info(cmdname, QX_FLAG_CMD, QX_FLAG_SKIP);
1687
1688 /* Check for fallback if not found */
1689 if (item == NULL) {
1690
1691 if (!strcasecmp(cmdname, "load.on")) {
1692 return instcmd("load.on.delay", "0");
1693 }
1694
1695 if (!strcasecmp(cmdname, "load.off")) {
1696 return instcmd("load.off.delay", "0");
1697 }
1698
1699 if (!strcasecmp(cmdname, "shutdown.return")) {
1700
1701 int ret;
1702
1703 /* Ensure "ups.start.auto" is set to "yes", if supported */
1704 if (dstate_getinfo("ups.start.auto")) {
1705 if (setvar("ups.start.auto", "yes") != STAT_SET_HANDLED) {
1706 upslogx(LOG_ERR, "%s: FAILED", __func__);
1707 return STAT_INSTCMD_FAILED;
1708 }
1709 }
1710
1711 ret = instcmd("load.on.delay", dstate_getinfo("ups.delay.start"));
1712 if (ret != STAT_INSTCMD_HANDLED) {
1713 return ret;
1714 }
1715
1716 return instcmd("load.off.delay", dstate_getinfo("ups.delay.shutdown"));
1717
1718 }
1719
1720 if (!strcasecmp(cmdname, "shutdown.stayoff")) {
1721
1722 int ret;
1723
1724 /* Ensure "ups.start.auto" is set to "no", if supported */
1725 if (dstate_getinfo("ups.start.auto")) {
1726 if (setvar("ups.start.auto", "no") != STAT_SET_HANDLED) {
1727 upslogx(LOG_ERR, "%s: FAILED", __func__);
1728 return STAT_INSTCMD_FAILED;
1729 }
1730 }
1731
1732 ret = instcmd("load.on.delay", "-1");
1733 if (ret != STAT_INSTCMD_HANDLED) {
1734 return ret;
1735 }
1736
1737 return instcmd("load.off.delay", dstate_getinfo("ups.delay.shutdown"));
1738
1739 }
1740
1741 upsdebugx(2, "%s: command %s unavailable", __func__, cmdname);
1742 return STAT_INSTCMD_INVALID;
1743 }
1744
1745 /* If extradata is empty, use the default value from the QX to NUT table, if any */
1746 extradata = extradata ? extradata : item->dfl;
1747 snprintf(value, sizeof(value), "%s", extradata ? extradata : "");
1748
1749 /* Preprocess command */
1750 if (item->preprocess != NULL && item->preprocess(item, value, sizeof(value))) {
1751 /* Something went wrong */
1752 upslogx(LOG_ERR, "%s: FAILED", __func__);
1753 return STAT_INSTCMD_FAILED;
1754 }
1755
1756 /* No preprocess function -> nothing to do with extradata */
1757 if (item->preprocess == NULL)
1758 snprintf(value, sizeof(value), "%s", "");
1759
1760 /* Send the command, get the reply */
1761 if (qx_process(item, strlen(value) > 0 ? value : NULL)) {
1762 /* Something went wrong */
1763 upslogx(LOG_ERR, "%s: FAILED", __func__);
1764 return STAT_INSTCMD_FAILED;
1765 }
1766
1767 /* We got a reply from the UPS: either subdriver->accepted (-> command handled) or the command itself echoed back (-> command failed) */
1768 if (strlen(item->value) > 0) {
1769
1770 if (subdriver->accepted != NULL && !strcasecmp(item->value, subdriver->accepted)) {
1771 upslogx(LOG_INFO, "%s: SUCCEED", __func__);
1772 /* Set the status so that SEMI_STATIC vars are polled */
1773 data_has_changed = TRUE;
1774 return STAT_INSTCMD_HANDLED;
1775 }
1776
1777 upslogx(LOG_ERR, "%s: FAILED", __func__);
1778 return STAT_INSTCMD_FAILED;
1779
1780 }
1781
1782 /* No reply from the UPS -> command handled */
1783 upslogx(LOG_INFO, "%s: SUCCEED", __func__);
1784 /* Set the status so that SEMI_STATIC vars are polled */
1785 data_has_changed = TRUE;
1786 return STAT_INSTCMD_HANDLED;
1787 }
1788
1789 /* See header file for details. */
setvar(const char * varname,const char * val)1790 int setvar(const char *varname, const char *val)
1791 {
1792 item_t *item;
1793 char value[SMALLBUF];
1794 st_tree_t *root = (st_tree_t *)dstate_getroot();
1795 int ok = 0;
1796
1797 /* Retrieve variable */
1798 item = find_nut_info(varname, QX_FLAG_SETVAR, QX_FLAG_SKIP);
1799
1800 if (item == NULL) {
1801 upsdebugx(2, "%s: element %s unavailable", __func__, varname);
1802 return STAT_SET_UNKNOWN;
1803 }
1804
1805 /* No NUT variable is available for this item, so we're handling a one-time setvar from ups.conf */
1806 if (item->qxflags & QX_FLAG_NONUT) {
1807
1808 const char *userval;
1809
1810 /* Nothing to do */
1811 if (!testvar(item->info_type)) {
1812 upsdebugx(2, "%s: nothing to do.. [%s]", __func__, item->info_type);
1813 return STAT_SET_HANDLED;
1814 }
1815
1816 userval = getval(item->info_type);
1817
1818 upslogx(LOG_INFO, "%s(%s, %s)", __func__, varname, userval ? userval : "[NULL]");
1819
1820 snprintf(value, sizeof(value), "%s", userval ? userval : "");
1821
1822 /* This item is available in NUT */
1823 } else {
1824
1825 upslogx(LOG_INFO, "%s(%s, %s)", __func__, varname, strlen(val) ? val : "[NULL]");
1826
1827 if (!strlen(val)) {
1828 upslogx(LOG_ERR, "%s: value not given for %s", __func__, item->info_type);
1829 return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */
1830 }
1831
1832 snprintf(value, sizeof(value), "%s", val);
1833
1834 /* Nothing to do */
1835 if (!strcasecmp(dstate_getinfo(item->info_type), value)) {
1836 upslogx(LOG_INFO, "%s: nothing to do.. [%s]", __func__, item->info_type);
1837 return STAT_SET_HANDLED;
1838 }
1839
1840 }
1841
1842 /* Check if given value is in the range of accepted values (range) */
1843 if (item->qxflags & QX_FLAG_RANGE) {
1844
1845 long valuetoset, min, max;
1846
1847 if (strspn(value, "0123456789 .") != strlen(value)) {
1848 upslogx(LOG_ERR, "%s: non numerical value [%s: %s]", __func__, item->info_type, value);
1849 return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */
1850 }
1851
1852 valuetoset = strtol(value, NULL, 10);
1853
1854 /* No NUT var is available for this item, so take its range from qx2nut table */
1855 if (item->qxflags & QX_FLAG_NONUT) {
1856
1857 info_rw_t *rvalue;
1858
1859 if (!strlen(value)) {
1860 upslogx(LOG_ERR, "%s: value not given for %s", __func__, item->info_type);
1861 return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */
1862 }
1863
1864 min = max = -1;
1865
1866 /* Loop on all existing values */
1867 for (rvalue = item->info_rw; rvalue != NULL && strlen(rvalue->value) > 0; rvalue++) {
1868
1869 if (rvalue->preprocess && rvalue->preprocess(rvalue->value, sizeof(rvalue->value)))
1870 continue;
1871
1872 if (min < 0) {
1873 min = strtol(rvalue->value, NULL, 10);
1874 continue;
1875 }
1876
1877 max = strtol(rvalue->value, NULL, 10);
1878
1879 /* valuetoset is in the range */
1880 if (min <= valuetoset && valuetoset <= max) {
1881 ok = 1;
1882 break;
1883 }
1884
1885 min = -1;
1886 max = -1;
1887
1888 }
1889
1890 /* We have a NUT var for this item, so check given value against the already set range */
1891 } else {
1892
1893 const range_t *range = state_getrangelist(root, item->info_type);
1894
1895 /* Unable to find tree node for var */
1896 if (!range) {
1897 upsdebugx(2, "%s: unable to find tree node for %s", __func__, item->info_type);
1898 return STAT_SET_UNKNOWN;
1899 }
1900
1901 while (range) {
1902
1903 min = range->min;
1904 max = range->max;
1905
1906 /* valuetoset is in the range */
1907 if (min <= valuetoset && valuetoset <= max) {
1908 ok = 1;
1909 break;
1910 }
1911 range = range->next;
1912 }
1913
1914 }
1915
1916 if (!ok) {
1917 upslogx(LOG_ERR, "%s: value out of range [%s: %s]", __func__, item->info_type, value);
1918 return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */
1919 }
1920
1921 /* Check if given value is in the range of accepted values (enum) */
1922 } else if (item->qxflags & QX_FLAG_ENUM) {
1923
1924 /* No NUT var is available for this item, so take its range from qx2nut table */
1925 if (item->qxflags & QX_FLAG_NONUT) {
1926
1927 info_rw_t *envalue;
1928
1929 if (!strlen(value)) {
1930 upslogx(LOG_ERR, "%s: value not given for %s", __func__, item->info_type);
1931 return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */
1932 }
1933
1934 /* Loop on all existing values */
1935 for (envalue = item->info_rw; envalue != NULL && strlen(envalue->value) > 0; envalue++) {
1936
1937 if (envalue->preprocess && envalue->preprocess(envalue->value, sizeof(envalue->value)))
1938 continue;
1939
1940 if (strcasecmp(envalue->value, value))
1941 continue;
1942
1943 /* value found */
1944 ok = 1;
1945 break;
1946
1947 }
1948
1949 /* We have a NUT var for this item, so check given value against the already set range */
1950 } else {
1951
1952 const enum_t *enumlist = state_getenumlist(root, item->info_type);
1953
1954 /* Unable to find tree node for var */
1955 if (!enumlist) {
1956 upsdebugx(2, "%s: unable to find tree node for %s", __func__, item->info_type);
1957 return STAT_SET_UNKNOWN;
1958 }
1959
1960 while (enumlist) {
1961
1962 /* If this is not the right value, go on to the next */
1963 if (strcasecmp(enumlist->val, value)) {
1964 enumlist = enumlist->next;
1965 continue;
1966 }
1967
1968 /* value found in enumlist */
1969 ok = 1;
1970 break;
1971 }
1972
1973 }
1974
1975 if (!ok) {
1976 upslogx(LOG_ERR, "%s: value out of range [%s: %s]", __func__, item->info_type, value);
1977 return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */
1978 }
1979
1980 /* Check if given value is not too long (string) */
1981 } else if (item->info_flags & ST_FLAG_STRING) {
1982
1983 const long aux = state_getaux(root, item->info_type);
1984
1985 /* Unable to find tree node for var */
1986 if (aux < 0) {
1987 upsdebugx(2, "%s: unable to find tree node for %s", __func__, item->info_type);
1988 return STAT_SET_UNKNOWN;
1989 }
1990
1991 /* FIXME? Should this cast to "long"?
1992 * An int-size string is quite a lot already,
1993 * even on architectures with a moderate INTMAX
1994 */
1995 if (aux < (int)strlen(value)) {
1996 upslogx(LOG_ERR, "%s: value is too long [%s: %s]", __func__, item->info_type, value);
1997 return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */
1998 }
1999
2000 }
2001
2002 /* Preprocess value: from NUT-compliant to UPS-compliant */
2003 if (item->preprocess != NULL && item->preprocess(item, value, sizeof(value))) {
2004 /* Something went wrong */
2005 upslogx(LOG_ERR, "%s: FAILED", __func__);
2006 return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */
2007 }
2008
2009 /* Handle server side variable */
2010 if (item->qxflags & QX_FLAG_ABSENT) {
2011 upsdebugx(2, "%s: setting server side variable %s", __func__, item->info_type);
2012 dstate_setinfo(item->info_type, "%s", value);
2013 upslogx(LOG_INFO, "%s: SUCCEED", __func__);
2014 return STAT_SET_HANDLED;
2015 }
2016
2017 /* No preprocess function -> nothing to do with val */
2018 if (item->preprocess == NULL)
2019 snprintf(value, sizeof(value), "%s", "");
2020
2021 /* Actual variable setting */
2022 if (qx_process(item, strlen(value) > 0 ? value : NULL)) {
2023 /* Something went wrong */
2024 upslogx(LOG_ERR, "%s: FAILED", __func__);
2025 return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */
2026 }
2027
2028 /* We got a reply from the UPS: either subdriver->accepted (-> command handled) or the command itself echoed back (-> command failed) */
2029 if (strlen(item->value) > 0) {
2030
2031 if (subdriver->accepted != NULL && !strcasecmp(item->value, subdriver->accepted)) {
2032 upslogx(LOG_INFO, "%s: SUCCEED", __func__);
2033 /* Set the status so that SEMI_STATIC vars are polled */
2034 data_has_changed = TRUE;
2035 return STAT_SET_HANDLED;
2036 }
2037
2038 upslogx(LOG_ERR, "%s: FAILED", __func__);
2039 return STAT_SET_UNKNOWN; /* TODO: HANDLED but FAILED, not UNKNOWN! */
2040
2041 }
2042
2043 /* No reply from the UPS -> command handled */
2044 upslogx(LOG_INFO, "%s: SUCCEED", __func__);
2045 /* Set the status so that SEMI_STATIC vars are polled */
2046 data_has_changed = TRUE;
2047 return STAT_SET_HANDLED;
2048 }
2049
2050 /* Try to shutdown the UPS */
2051 void upsdrv_shutdown(void)
2052 __attribute__((noreturn));
2053
upsdrv_shutdown(void)2054 void upsdrv_shutdown(void)
2055 {
2056 int retry;
2057 item_t *item;
2058 const char *val;
2059
2060 upsdebugx(1, "%s...", __func__);
2061
2062 /* Get user-defined delays */
2063
2064 /* Start delay */
2065 item = find_nut_info("ups.delay.start", 0, QX_FLAG_SKIP);
2066
2067 /* Don't know what happened */
2068 if (!item)
2069 fatalx(EXIT_FAILURE, "Unable to set start delay");
2070
2071 /* Set the default value */
2072 dstate_setinfo(item->info_type, "%s", item->dfl);
2073
2074 /* Set var flags/range/enum */
2075 qx_set_var(item);
2076
2077 /* Retrieve user defined delay settings */
2078 val = getval(QX_VAR_ONDELAY);
2079
2080 if (val && setvar(item->info_type, val) != STAT_SET_HANDLED) {
2081 fatalx(EXIT_FAILURE, "Start delay '%s' out of range", val);
2082 }
2083
2084 /* Shutdown delay */
2085 item = find_nut_info("ups.delay.shutdown", 0, QX_FLAG_SKIP);
2086
2087 /* Don't know what happened */
2088 if (!item)
2089 fatalx(EXIT_FAILURE, "Unable to set shutdown delay");
2090
2091 /* Set the default value */
2092 dstate_setinfo(item->info_type, "%s", item->dfl);
2093
2094 /* Set var flags/range/enum */
2095 qx_set_var(item);
2096
2097 /* Retrieve user defined delay settings */
2098 val = getval(QX_VAR_OFFDELAY);
2099
2100 if (val && setvar(item->info_type, val) != STAT_SET_HANDLED) {
2101 fatalx(EXIT_FAILURE, "Shutdown delay '%s' out of range", val);
2102 }
2103
2104 /* Stop pending shutdowns */
2105 if (find_nut_info("shutdown.stop", QX_FLAG_CMD, QX_FLAG_SKIP)) {
2106
2107 for (retry = 1; retry <= MAXTRIES; retry++) {
2108
2109 if (instcmd("shutdown.stop", NULL) != STAT_INSTCMD_HANDLED) {
2110 continue;
2111 }
2112
2113 break;
2114
2115 }
2116
2117 if (retry > MAXTRIES) {
2118 upslogx(LOG_NOTICE, "No shutdown pending");
2119 }
2120
2121 }
2122
2123 /* Shutdown */
2124 for (retry = 1; retry <= MAXTRIES; retry++) {
2125
2126 if (testvar("stayoff")) {
2127
2128 if (instcmd("shutdown.stayoff", NULL) != STAT_INSTCMD_HANDLED) {
2129 continue;
2130 }
2131
2132 } else {
2133
2134 if (instcmd("shutdown.return", NULL) != STAT_INSTCMD_HANDLED) {
2135 continue;
2136 }
2137
2138 }
2139
2140 fatalx(EXIT_SUCCESS, "Shutting down in %s seconds", dstate_getinfo("ups.delay.shutdown"));
2141 }
2142
2143 fatalx(EXIT_FAILURE, "Shutdown failed!");
2144 }
2145
2146 #ifdef QX_USB
2147 #ifndef TESTING
2148 static const struct {
2149 const char *name;
2150 int (*command)(const char *cmd, char *buf, size_t buflen);
2151 } usbsubdriver[] = {
2152 { "cypress", &cypress_command },
2153 { "phoenixtec", &phoenixtec_command },
2154 { "phoenix", &phoenix_command },
2155 { "ippon", &ippon_command },
2156 { "krauler", &krauler_command },
2157 { "fabula", &fabula_command },
2158 { "hunnox", &hunnox_command },
2159 { "fuji", &fuji_command },
2160 { "sgs", &sgs_command },
2161 { "snr", &snr_command },
2162 { NULL, NULL }
2163 };
2164 #endif
2165 #endif
2166
2167
upsdrv_help(void)2168 void upsdrv_help(void)
2169 {
2170 #ifdef QX_USB
2171 #ifndef TESTING
2172 printf("\nAcceptable values for 'subdriver' via -x or ups.conf in this driver: ");
2173 size_t i;
2174
2175 for (i = 0; usbsubdriver[i].name != NULL; i++) {
2176 if (i>0)
2177 printf(", ");
2178 printf("%s", usbsubdriver[i].name);
2179 }
2180 printf("\n\n");
2181 #endif
2182 #endif
2183
2184 printf("Read The Fine Manual ('man 8 nutdrv_qx')\n");
2185 }
2186
2187 /* Adding flags/vars */
upsdrv_makevartable(void)2188 void upsdrv_makevartable(void)
2189 {
2190 char temp[SMALLBUF];
2191 int i;
2192
2193 upsdebugx(1, "%s...", __func__);
2194
2195 snprintf(temp, sizeof(temp), "Set shutdown delay, in seconds (default=%s)", DEFAULT_OFFDELAY);
2196 addvar(VAR_VALUE, QX_VAR_OFFDELAY, temp);
2197
2198 snprintf(temp, sizeof(temp), "Set startup delay, in seconds (default=%s)", DEFAULT_ONDELAY);
2199 addvar(VAR_VALUE, QX_VAR_ONDELAY, temp);
2200
2201 addvar(VAR_FLAG, "stayoff", "If invoked the UPS won't return after a shutdown when FSD arises");
2202
2203 snprintf(temp, sizeof(temp), "Set polling frequency, in seconds, to reduce data flow (default=%d)", DEFAULT_POLLFREQ);
2204 addvar(VAR_VALUE, QX_VAR_POLLFREQ, temp);
2205
2206 addvar(VAR_VALUE, "protocol", "Preselect communication protocol (skip autodetection)");
2207
2208 /* battery.{charge,runtime} guesstimation */
2209 addvar(VAR_VALUE, "runtimecal", "Parameters used for runtime calculation");
2210 addvar(VAR_VALUE, "chargetime", "Nominal charge time for UPS battery");
2211 addvar(VAR_VALUE, "idleload", "Minimum load to be used for runtime calculation");
2212
2213 #ifdef QX_USB
2214 addvar(VAR_VALUE, "subdriver", "Serial-over-USB subdriver selection");
2215 /* allow -x vendor=X, vendorid=X, product=X, productid=X, serial=X */
2216 nut_usb_addvars();
2217
2218 addvar(VAR_VALUE, "langid_fix", "Apply the language ID workaround to the krauler subdriver (0x409 or 0x4095)");
2219 addvar(VAR_FLAG, "noscanlangid", "Don't autoscan valid range for langid");
2220 #endif /* QX_USB */
2221
2222 #ifdef QX_SERIAL
2223 addvar(VAR_VALUE, "cablepower", "Set cable power for serial interface");
2224 #endif /* QX_SERIAL */
2225
2226 /* Subdrivers flags/vars */
2227 for (i = 0; subdriver_list[i] != NULL; i++) {
2228
2229 if (subdriver_list[i]->makevartable != NULL)
2230 subdriver_list[i]->makevartable();
2231
2232 }
2233 }
2234
2235 /* Update UPS status/infos */
upsdrv_updateinfo(void)2236 void upsdrv_updateinfo(void)
2237 {
2238 time_t now;
2239 static int retry = 0;
2240
2241 upsdebugx(1, "%s...", __func__);
2242
2243 time(&now);
2244
2245 /* Clear status buffer before beginning */
2246 status_init();
2247
2248 /* Do a full update (polling) every pollfreq or upon data change (i.e. setvar/instcmd) */
2249 if ((now > (lastpoll + pollfreq)) || (data_has_changed == TRUE)) {
2250
2251 upsdebugx(1, "Full update...");
2252
2253 /* Clear ups_status */
2254 ups_status = 0;
2255
2256 alarm_init();
2257
2258 if (qx_ups_walk(QX_WALKMODE_FULL_UPDATE) == FALSE) {
2259
2260 if (retry < MAXTRIES || retry == MAXTRIES) {
2261 upsdebugx(1, "Communications with the UPS lost: status read failed!");
2262 retry++;
2263 } else {
2264 dstate_datastale();
2265 }
2266
2267 return;
2268 }
2269
2270 lastpoll = now;
2271 data_has_changed = FALSE;
2272
2273 ups_alarm_set();
2274 alarm_commit();
2275
2276 } else {
2277
2278 upsdebugx(1, "Quick update...");
2279
2280 /* Quick poll data only to see if the UPS is still connected */
2281 if (qx_ups_walk(QX_WALKMODE_QUICK_UPDATE) == FALSE) {
2282
2283 if (retry < MAXTRIES || retry == MAXTRIES) {
2284 upsdebugx(1, "Communications with the UPS lost: status read failed!");
2285 retry++;
2286 } else {
2287 dstate_datastale();
2288 }
2289
2290 return;
2291 }
2292
2293 }
2294
2295 ups_status_set();
2296 status_commit();
2297
2298 if (retry > MAXTRIES) {
2299 upslogx(LOG_NOTICE, "Communications with the UPS re-established");
2300 }
2301
2302 retry = 0;
2303
2304 dstate_dataok();
2305 }
2306
2307 /* Initialise data from UPS */
upsdrv_initinfo(void)2308 void upsdrv_initinfo(void)
2309 {
2310 char *val;
2311
2312 upsdebugx(1, "%s...", __func__);
2313
2314 dstate_setinfo("driver.version.data", "%s", subdriver->name);
2315
2316 /* Initialise data */
2317 if (qx_ups_walk(QX_WALKMODE_INIT) == FALSE) {
2318 fatalx(EXIT_FAILURE, "Can't initialise data from the UPS");
2319 }
2320
2321 /* Init battery guesstimation */
2322 qx_initbattery();
2323
2324 if (dstate_getinfo("ups.delay.start")) {
2325
2326 /* Retrieve user defined delay settings */
2327 val = getval(QX_VAR_ONDELAY);
2328
2329 if (val && setvar("ups.delay.start", val) != STAT_SET_HANDLED) {
2330 fatalx(EXIT_FAILURE, "Start delay '%s' out of range", val);
2331 }
2332
2333 }
2334
2335 if (dstate_getinfo("ups.delay.shutdown")) {
2336
2337 /* Retrieve user defined delay settings */
2338 val = getval(QX_VAR_OFFDELAY);
2339
2340 if (val && setvar("ups.delay.shutdown", val) != STAT_SET_HANDLED) {
2341 fatalx(EXIT_FAILURE, "Shutdown delay '%s' out of range", val);
2342 }
2343
2344 }
2345
2346 if (!find_nut_info("load.off", QX_FLAG_CMD, QX_FLAG_SKIP) && find_nut_info("load.off.delay", QX_FLAG_CMD, QX_FLAG_SKIP)) {
2347 /* Adds default with a delay value of '0' (= immediate) */
2348 dstate_addcmd("load.off");
2349 }
2350
2351 if (!find_nut_info("load.on", QX_FLAG_CMD, QX_FLAG_SKIP) && find_nut_info("load.on.delay", QX_FLAG_CMD, QX_FLAG_SKIP)) {
2352 /* Adds default with a delay value of '0' (= immediate) */
2353 dstate_addcmd("load.on");
2354 }
2355
2356 /* Init polling frequency */
2357 val = getval(QX_VAR_POLLFREQ);
2358 if (val)
2359 pollfreq = strtol(val, NULL, 10);
2360
2361 dstate_setinfo("driver.parameter.pollfreq", "%ld", pollfreq);
2362
2363 time(&lastpoll);
2364
2365 /* Install handlers */
2366 upsh.setvar = setvar;
2367 upsh.instcmd = instcmd;
2368
2369 /* Subdriver initinfo */
2370 if (subdriver->initinfo != NULL)
2371 subdriver->initinfo();
2372 }
2373
2374 /* Open the port and the like and choose the subdriver */
upsdrv_initups(void)2375 void upsdrv_initups(void)
2376 {
2377 upsdebugx(1, "%s...", __func__);
2378
2379 #if defined(QX_SERIAL) && defined(QX_USB)
2380
2381 /* Whether the device is connected through USB or serial */
2382 if (
2383 !strcasecmp(dstate_getinfo("driver.parameter.port"), "auto") ||
2384 getval("subdriver") ||
2385 getval("vendorid") ||
2386 getval("productid") ||
2387 getval("vendor") ||
2388 getval("product") ||
2389 getval("serial") ||
2390 getval("bus") ||
2391 getval("langid_fix")
2392 ) {
2393 /* USB */
2394 is_usb = 1;
2395 } else {
2396 /* Serial */
2397 is_usb = 0;
2398 }
2399
2400 #endif /* QX_SERIAL && QX_USB */
2401
2402 /* Serial */
2403 #ifdef QX_SERIAL
2404
2405 #ifdef QX_USB
2406 if (!is_usb) {
2407 #endif /* QX_USB */
2408
2409 #ifndef TESTING
2410
2411 const struct {
2412 const char *val;
2413 const int dtr;
2414 const int rts;
2415 } cablepower[] = {
2416 { "normal", 1, 0 }, /* Default */
2417 { "reverse", 0, 1 },
2418 { "both", 1, 1 },
2419 { "none", 0, 0 },
2420 { NULL, 0, 0 }
2421 };
2422
2423 int i;
2424 const char *val;
2425 struct termios tio;
2426
2427 /* Open and lock the serial port and set the speed to 2400 baud. */
2428 upsfd = ser_open(device_path);
2429 ser_set_speed(upsfd, device_path, B2400);
2430
2431 if (tcgetattr(upsfd, &tio)) {
2432 fatal_with_errno(EXIT_FAILURE, "tcgetattr");
2433 }
2434
2435 /* Use canonical mode input processing (to read reply line) */
2436 tio.c_lflag |= ICANON; /* Canonical input (erase and kill processing) */
2437
2438 tio.c_cc[VEOF] = _POSIX_VDISABLE;
2439 tio.c_cc[VEOL] = '\r';
2440 tio.c_cc[VERASE] = _POSIX_VDISABLE;
2441 tio.c_cc[VINTR] = _POSIX_VDISABLE;
2442 tio.c_cc[VKILL] = _POSIX_VDISABLE;
2443 tio.c_cc[VQUIT] = _POSIX_VDISABLE;
2444 tio.c_cc[VSUSP] = _POSIX_VDISABLE;
2445 tio.c_cc[VSTART] = _POSIX_VDISABLE;
2446 tio.c_cc[VSTOP] = _POSIX_VDISABLE;
2447
2448 if (tcsetattr(upsfd, TCSANOW, &tio)) {
2449 fatal_with_errno(EXIT_FAILURE, "tcsetattr");
2450 }
2451
2452 val = getval("cablepower");
2453 for (i = 0; val && cablepower[i].val; i++) {
2454
2455 if (!strcasecmp(val, cablepower[i].val)) {
2456 break;
2457 }
2458 }
2459
2460 if (!cablepower[i].val) {
2461 fatalx(EXIT_FAILURE, "Value '%s' not valid for 'cablepower'", val);
2462 }
2463
2464 ser_set_dtr(upsfd, cablepower[i].dtr);
2465 ser_set_rts(upsfd, cablepower[i].rts);
2466
2467 /* Allow some time to settle for the cablepower */
2468 usleep(100000);
2469
2470 #endif /* TESTING */
2471
2472 #ifdef QX_USB
2473 } else { /* is_usb */
2474 #endif /* QX_USB */
2475
2476 #endif /* QX_SERIAL */
2477
2478 /* USB */
2479 #ifdef QX_USB
2480
2481 #ifndef TESTING
2482 int ret, langid;
2483 char tbuf[255]; /* Some devices choke on size > 255 */
2484 char *regex_array[7];
2485
2486 char *subdrv = getval("subdriver");
2487
2488 regex_array[0] = getval("vendorid");
2489 regex_array[1] = getval("productid");
2490 regex_array[2] = getval("vendor");
2491 regex_array[3] = getval("product");
2492 regex_array[4] = getval("serial");
2493 regex_array[5] = getval("bus");
2494 regex_array[6] = getval("device");
2495
2496 /* Check for language ID workaround (#1) */
2497 if (getval("langid_fix")) {
2498 /* Skip "0x" prefix and set back to hexadecimal */
2499 unsigned int u_langid_fix;
2500 if ( (sscanf(getval("langid_fix") + 2, "%x", &u_langid_fix) != 1)
2501 || (u_langid_fix > INT_MAX)
2502 ) {
2503 upslogx(LOG_NOTICE, "Error enabling language ID workaround");
2504 } else {
2505 langid_fix = (int)u_langid_fix;
2506 upsdebugx(2, "Language ID workaround enabled (using '0x%x')", langid_fix);
2507 }
2508 }
2509
2510 /* Pick up the subdriver name if set explicitly */
2511 if (subdrv) {
2512
2513 int i;
2514
2515 if (!regex_array[0] || !regex_array[1]) {
2516 fatalx(EXIT_FAILURE, "When specifying a subdriver, 'vendorid' and 'productid' are mandatory.");
2517 }
2518
2519 for (i = 0; usbsubdriver[i].name; i++) {
2520
2521 if (strcasecmp(subdrv, usbsubdriver[i].name)) {
2522 continue;
2523 }
2524
2525 subdriver_command = usbsubdriver[i].command;
2526 break;
2527 }
2528
2529 if (!subdriver_command) {
2530 fatalx(EXIT_FAILURE, "Subdriver '%s' not found!", subdrv);
2531 }
2532
2533 }
2534
2535 ret = USBNewRegexMatcher(®ex_matcher, regex_array, REG_ICASE | REG_EXTENDED);
2536 switch (ret)
2537 {
2538 case -1:
2539 fatal_with_errno(EXIT_FAILURE, "USBNewRegexMatcher");
2540 case 0:
2541 break; /* All is well */
2542 default:
2543 fatalx(EXIT_FAILURE, "Invalid regular expression: %s", regex_array[ret]);
2544 }
2545
2546 /* Link the matchers */
2547 regex_matcher->next = &device_matcher;
2548
2549 ret = usb->open(&udev, &usbdevice, regex_matcher, NULL);
2550 if (ret < 0) {
2551 fatalx(EXIT_FAILURE,
2552 "No supported devices found. Please check your device availability with 'lsusb'\n"
2553 "and make sure you have an up-to-date version of NUT. If this does not help,\n"
2554 "try running the driver with at least 'subdriver', 'vendorid' and 'productid'\n"
2555 "options specified. Please refer to the man page for details about these options\n"
2556 "(man 8 nutdrv_qx).\n");
2557 }
2558
2559 if (!subdriver_command) {
2560 fatalx(EXIT_FAILURE, "No subdriver selected");
2561 }
2562
2563 /* Create a new matcher for later reopening */
2564 ret = USBNewExactMatcher(&reopen_matcher, &usbdevice);
2565 if (ret) {
2566 fatal_with_errno(EXIT_FAILURE, "USBNewExactMatcher");
2567 }
2568
2569 /* Link the matchers */
2570 reopen_matcher->next = regex_matcher;
2571
2572 dstate_setinfo("ups.vendorid", "%04x", usbdevice.VendorID);
2573 dstate_setinfo("ups.productid", "%04x", usbdevice.ProductID);
2574
2575 /* Check for language ID workaround (#2) */
2576 if ((langid_fix != -1) && (!getval("noscanlangid"))) {
2577 /* Future improvement:
2578 * Asking for the zero'th index is special - it returns a string descriptor that contains all the language IDs supported by the device.
2579 * Typically there aren't many - often only one.
2580 * The language IDs are 16 bit numbers, and they start at the third byte in the descriptor.
2581 * See USB 2.0 specification, section 9.6.7, for more information on this.
2582 * This should allow automatic application of the workaround */
2583 ret = usb_get_string(udev, 0, 0, tbuf, sizeof(tbuf));
2584 if (ret >= 4) {
2585 langid = tbuf[2] | (tbuf[3] << 8);
2586 upsdebugx(1, "First supported language ID: 0x%x (please report to the NUT maintainer!)", langid);
2587 }
2588 }
2589
2590 #endif /* TESTING */
2591
2592 #ifdef QX_SERIAL
2593 } /* is_usb */
2594 #endif /* QX_SERIAL */
2595
2596 #endif /* QX_USB */
2597
2598 /* Choose subdriver */
2599 if (!subdriver_matcher())
2600 fatalx(EXIT_FAILURE, "Device not supported!");
2601
2602 /* Subdriver initups */
2603 if (subdriver->initups != NULL)
2604 subdriver->initups();
2605 }
2606
2607 /* Close the ports and the like */
upsdrv_cleanup(void)2608 void upsdrv_cleanup(void)
2609 {
2610 upsdebugx(1, "%s...", __func__);
2611
2612 #ifndef TESTING
2613
2614 #ifdef QX_SERIAL
2615
2616 #ifdef QX_USB
2617 if (!is_usb) {
2618 #endif /* QX_USB */
2619
2620 ser_set_dtr(upsfd, 0);
2621 ser_close(upsfd, device_path);
2622
2623 #ifdef QX_USB
2624 } else { /* is_usb */
2625 #endif /* QX_USB */
2626
2627 #endif /* QX_SERIAL */
2628
2629 #ifdef QX_USB
2630
2631 usb->close(udev);
2632 USBFreeExactMatcher(reopen_matcher);
2633 USBFreeRegexMatcher(regex_matcher);
2634 free(usbdevice.Vendor);
2635 free(usbdevice.Product);
2636 free(usbdevice.Serial);
2637 free(usbdevice.Bus);
2638 free(usbdevice.Device);
2639
2640 #ifdef QX_SERIAL
2641 } /* is_usb */
2642 #endif /* QX_SERIAL */
2643
2644 #endif /* QX_USB */
2645
2646 #endif /* TESTING */
2647
2648 }
2649
2650
2651 /* == Support functions == */
2652
2653 /* Generic command processing function: send a command and read a reply.
2654 * Returns < 0 on error, 0 on timeout and the number of bytes read on success. */
qx_command(const char * cmd,char * buf,size_t buflen)2655 static ssize_t qx_command(const char *cmd, char *buf, size_t buflen)
2656 {
2657 /* NOTE: Could not find in which ifdef-ed codepath, but clang complained
2658 * about unused parameters here. Reference them just in case...
2659 */
2660 NUT_UNUSED_VARIABLE(cmd);
2661 NUT_UNUSED_VARIABLE(buf);
2662 NUT_UNUSED_VARIABLE(buflen);
2663
2664 #ifndef TESTING
2665
2666 ssize_t ret = -1;
2667
2668 # ifdef QX_USB
2669
2670 # ifdef QX_SERIAL
2671 /* Communication: USB */
2672 if (is_usb) {
2673 # endif /* QX_SERIAL (&& QX_USB)*/
2674
2675 if (udev == NULL) {
2676 ret = usb->open(&udev, &usbdevice, reopen_matcher, NULL);
2677
2678 if (ret < 1) {
2679 return ret;
2680 }
2681 }
2682
2683 ret = (*subdriver_command)(cmd, buf, buflen);
2684
2685 if (ret >= 0) {
2686 return ret;
2687 }
2688
2689 switch (ret)
2690 {
2691 case -EBUSY: /* Device or resource busy */
2692 fatal_with_errno(EXIT_FAILURE, "Got disconnected by another driver");
2693 #ifndef HAVE___ATTRIBUTE__NORETURN
2694 # if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE)
2695 # pragma GCC diagnostic push
2696 # pragma GCC diagnostic ignored "-Wunreachable-code"
2697 # endif
2698 exit(EXIT_FAILURE); /* Should not get here in practice, but compiler is afraid we can fall through */
2699 # if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE)
2700 # pragma GCC diagnostic pop
2701 # endif
2702 #endif
2703
2704 case -EPERM: /* Operation not permitted */
2705 fatal_with_errno(EXIT_FAILURE, "Permissions problem");
2706 #ifndef HAVE___ATTRIBUTE__NORETURN
2707 # if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE)
2708 # pragma GCC diagnostic push
2709 # pragma GCC diagnostic ignored "-Wunreachable-code"
2710 # endif
2711 exit(EXIT_FAILURE); /* Should not get here in practice, but compiler is afraid we can fall through */
2712 # if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_UNREACHABLE_CODE)
2713 # pragma GCC diagnostic pop
2714 # endif
2715 #endif
2716
2717 case -EPIPE: /* Broken pipe */
2718 if (usb_clear_halt(udev, 0x81) == 0) {
2719 upsdebugx(1, "Stall condition cleared");
2720 break;
2721 }
2722 #ifdef ETIME
2723 goto fallthrough_case_ETIME;
2724 case -ETIME: /* Timer expired */
2725 fallthrough_case_ETIME:
2726 #endif /* ETIME */
2727 if (usb_reset(udev) == 0) {
2728 upsdebugx(1, "Device reset handled");
2729 }
2730 goto fallthrough_case_reconnect;
2731 case -ENODEV: /* No such device */
2732 case -EACCES: /* Permission denied */
2733 case -EIO: /* I/O error */
2734 case -ENXIO: /* No such device or address */
2735 case -ENOENT: /* No such file or directory */
2736 fallthrough_case_reconnect:
2737 /* Uh oh, got to reconnect! */
2738 usb->close(udev);
2739 udev = NULL;
2740 break;
2741
2742 case -ETIMEDOUT: /* Connection timed out */
2743 case -EOVERFLOW: /* Value too large for defined data type */
2744 #ifdef EPROTO
2745 case -EPROTO: /* Protocol error */
2746 #endif
2747 default:
2748 break;
2749 }
2750
2751 # ifdef QX_SERIAL
2752 /* Communication: serial */
2753 } else { /* !is_usb */
2754 # endif /* QX_SERIAL (&& QX_USB) */
2755
2756 # endif /* QX_USB (&& TESTING) */
2757
2758 # ifdef QX_SERIAL
2759
2760 ser_flush_io(upsfd);
2761
2762 ret = ser_send(upsfd, "%s", cmd);
2763
2764 if (ret <= 0) {
2765 upsdebugx(3, "send: %s (%zd)", ret ? strerror(errno) : "timeout", ret);
2766 return ret;
2767 }
2768
2769 upsdebugx(3, "send: '%.*s'", (int)strcspn(cmd, "\r"), cmd);
2770
2771 ret = ser_get_buf(upsfd, buf, buflen, SER_WAIT_SEC, 0);
2772
2773 if (ret <= 0) {
2774 upsdebugx(3, "read: %s (%zd)", ret ? strerror(errno) : "timeout", ret);
2775 return ret;
2776 }
2777
2778 upsdebug_hex(5, "read", buf, (size_t)ret);
2779 upsdebugx(3, "read: '%.*s'", (int)strcspn(buf, "\r"), buf);
2780
2781 # ifdef QX_USB
2782 } /* !is_usb */
2783 # endif /* QX_USB (&& QX_SERIAL) */
2784
2785 # endif /* QX_SERIAL (&& TESTING) */
2786
2787 return ret;
2788
2789 #else /* TESTING */
2790
2791 testing_t *testing = subdriver->testing;
2792 int i;
2793
2794 memset(buf, 0, buflen);
2795
2796 upsdebugx(3, "send: '%.*s'", (int)strcspn(cmd, "\r"), cmd);
2797
2798 for (i = 0; cmd && testing[i].cmd; i++) {
2799
2800 if (strcasecmp(cmd, testing[i].cmd)) {
2801 continue;
2802 }
2803
2804 upsdebugx(3, "read: '%.*s'", (int)strcspn(testing[i].answer, "\r"), testing[i].answer);
2805
2806 /* If requested to do so and this is the case, try to preserve inner '\0's (treat answer as a sequence of bytes) */
2807 if (testing[i].answer_len > 0 && strlen(testing[i].answer) < (size_t)testing[i].answer_len) {
2808
2809 size_t len;
2810
2811 len = buflen <= (size_t)testing[i].answer_len ? buflen - 1 : (size_t)testing[i].answer_len;
2812 len = len <= sizeof(testing[i].answer) ? len : sizeof(testing[i].answer);
2813
2814 memcpy(buf, testing[i].answer, len);
2815 upsdebug_hex(4, "read", buf, (int)len);
2816
2817 return len;
2818
2819 }
2820
2821 return snprintf(buf, buflen, "%s", testing[i].answer);
2822
2823 }
2824
2825 /* If the driver expects some kind of reply in case of error.. */
2826 if (subdriver->rejected != NULL) {
2827
2828 /* ..fulfill its expectations.. */
2829 upsdebugx(3, "read: '%.*s'", (int)strcspn(subdriver->rejected, "\r"), subdriver->rejected);
2830 return snprintf(buf, buflen, "%s", subdriver->rejected);
2831
2832 /* ..otherwise.. */
2833 } else {
2834
2835 /* ..echo back the command */
2836 upsdebugx(3, "read: '%.*s'", (int)strcspn(cmd, "\r"), cmd);
2837 return snprintf(buf, buflen, "%s", cmd);
2838
2839 }
2840
2841 #endif /* TESTING */
2842 }
2843
2844 /* See header file for details.
2845 * Interpretation is done in ups_status_set(). */
update_status(const char * value)2846 void update_status(const char *value)
2847 {
2848 status_lkp_t *status_item;
2849 int clear = 0;
2850
2851 upsdebugx(5, "%s: %s", __func__, value);
2852
2853 if (*value == '!') {
2854 value++;
2855 clear = 1;
2856 }
2857
2858 for (status_item = status_info; status_item->status_str != NULL ; status_item++) {
2859
2860 if (strcasecmp(status_item->status_str, value))
2861 continue;
2862
2863 if (clear) {
2864 ups_status &= ~status_item->status_mask;
2865 } else {
2866 ups_status |= status_item->status_mask;
2867 }
2868
2869 return;
2870 }
2871
2872 upsdebugx(5, "%s: Warning! %s not in list of known values", __func__, value);
2873 }
2874
2875 /* Choose subdriver */
subdriver_matcher(void)2876 static int subdriver_matcher(void)
2877 {
2878 const char *protocol = getval("protocol");
2879 int i;
2880
2881 /* Select the subdriver for this device */
2882 for (i = 0; subdriver_list[i] != NULL; i++) {
2883
2884 int j;
2885
2886 /* If protocol is set in ups.conf, use it */
2887 if (protocol) {
2888
2889 char subdrv_name[SMALLBUF];
2890
2891 /* Get rid of subdriver version */
2892 snprintf(subdrv_name, sizeof(subdrv_name), "%.*s", (int)strcspn(subdriver_list[i]->name, " "), subdriver_list[i]->name);
2893
2894 if (strcasecmp(subdrv_name, protocol)) {
2895 upsdebugx(2, "Skipping protocol %s", subdriver_list[i]->name);
2896 continue;
2897 }
2898
2899 }
2900
2901 /* Give every subdriver some tries */
2902 for (j = 0; j < MAXTRIES; j++) {
2903
2904 subdriver = subdriver_list[i];
2905
2906 if (subdriver->claim()) {
2907 break;
2908 }
2909
2910 subdriver = NULL;
2911
2912 }
2913
2914 if (subdriver != NULL)
2915 break;
2916
2917 }
2918
2919 if (!subdriver) {
2920 upslogx(LOG_ERR, "Device not supported!");
2921 return 0;
2922 }
2923
2924 upslogx(LOG_INFO, "Using protocol: %s", subdriver->name);
2925
2926 return 1;
2927 }
2928
2929 /* Set vars boundaries */
qx_set_var(item_t * item)2930 static void qx_set_var(item_t *item)
2931 {
2932 if (!(item->qxflags & QX_FLAG_NONUT))
2933 dstate_setflags(item->info_type, item->info_flags);
2934
2935 /* Set max length for strings, if needed */
2936 if (item->info_flags & ST_FLAG_STRING && !(item->qxflags & QX_FLAG_NONUT))
2937 dstate_setaux(item->info_type, strtol(item->info_rw[0].value, NULL, 10));
2938
2939 /* Set enum list */
2940 if (item->qxflags & QX_FLAG_ENUM) {
2941
2942 info_rw_t *envalue;
2943 char buf[LARGEBUF] = "";
2944
2945 /* Loop on all existing values */
2946 for (envalue = item->info_rw; envalue != NULL && strlen(envalue->value) > 0; envalue++) {
2947
2948 if (envalue->preprocess && envalue->preprocess(envalue->value, sizeof(envalue->value)))
2949 continue;
2950
2951 /* This item is not available yet in NUT, so publish these data in the logs */
2952 if (item->qxflags & QX_FLAG_NONUT) {
2953
2954 snprintfcat(buf, sizeof(buf), " %s", envalue->value);
2955
2956 /* This item is available in NUT, add its enum to the variable */
2957 } else {
2958
2959 dstate_addenum(item->info_type, "%s", envalue->value);
2960
2961 }
2962
2963 }
2964
2965 if (item->qxflags & QX_FLAG_NONUT)
2966 upslogx(LOG_INFO, "%s, settable values:%s", item->info_type, strlen(buf) > 0 ? buf : " none");
2967
2968 }
2969
2970 /* Set range */
2971 if (item->qxflags & QX_FLAG_RANGE) {
2972
2973 info_rw_t *rvalue, *from = NULL, *to = NULL;
2974 int ok = 0;
2975
2976 /* Loop on all existing values */
2977 for (rvalue = item->info_rw; rvalue != NULL && strlen(rvalue->value) > 0; rvalue++) {
2978
2979 if (rvalue->preprocess && rvalue->preprocess(rvalue->value, sizeof(rvalue->value)))
2980 continue;
2981
2982 if (!from) {
2983 from = rvalue;
2984 continue;
2985 }
2986
2987 to = rvalue;
2988
2989 /* This item is not available yet in NUT, so publish these data in the logs */
2990 if (item->qxflags & QX_FLAG_NONUT) {
2991
2992 upslogx(LOG_INFO, "%s, settable range: %s..%s",
2993 item->info_type, from->value, to->value);
2994 ok++;
2995
2996 /* This item is available in NUT, add its range to the variable */
2997 } else {
2998 long lFrom = strtol(from->value, NULL, 10),
2999 lTo = strtol(to->value, NULL, 10);
3000
3001 if (lFrom > INT_MAX || lTo > INT_MAX) {
3002 upslogx(LOG_INFO,
3003 "%s, settable range exceeds INT_MAX: %ld..%ld",
3004 item->info_type, lFrom, lTo);
3005 } else {
3006 dstate_addrange(item->info_type, (int)lFrom, (int)lTo);
3007 }
3008 }
3009
3010 from = NULL;
3011 to = NULL;
3012
3013 }
3014
3015 /* This item is not available yet in NUT and we weren't able to get its range; let people know it */
3016 if ((item->qxflags & QX_FLAG_NONUT) && !ok)
3017 upslogx(LOG_INFO, "%s, settable range: none", item->info_type);
3018
3019 }
3020 }
3021
3022 /* Walk UPS variables and set elements of the qx2nut array. */
qx_ups_walk(walkmode_t mode)3023 static bool_t qx_ups_walk(walkmode_t mode)
3024 {
3025 item_t *item;
3026 int retcode;
3027
3028 /* Clear batt.{chrg,runt}.act for guesstimation */
3029 if (mode == QX_WALKMODE_FULL_UPDATE) {
3030 batt.runt.act = -1;
3031 batt.chrg.act = -1;
3032 }
3033
3034 /* Clear data from previous_item */
3035 memset(previous_item.command, 0, sizeof(previous_item.command));
3036 memset(previous_item.answer, 0, sizeof(previous_item.answer));
3037
3038 /* 3 modes: QX_WALKMODE_INIT, QX_WALKMODE_QUICK_UPDATE and QX_WALKMODE_FULL_UPDATE */
3039
3040 /* Device data walk */
3041 for (item = subdriver->qx2nut; item->info_type != NULL; item++) {
3042
3043 /* Skip this item */
3044 if (item->qxflags & QX_FLAG_SKIP)
3045 continue;
3046
3047 upsdebugx(10, "%s: processing: %s", __func__, item->info_type);
3048
3049 /* Filter data according to mode */
3050 switch (mode)
3051 {
3052 /* Device capabilities enumeration */
3053 case QX_WALKMODE_INIT:
3054
3055 /* Special case for handling server side variables */
3056 if (item->qxflags & QX_FLAG_ABSENT) {
3057
3058 /* Already set */
3059 if (dstate_getinfo(item->info_type))
3060 continue;
3061
3062 dstate_setinfo(item->info_type, "%s", item->dfl);
3063
3064 /* Set var flags/range/enum */
3065 qx_set_var(item);
3066
3067 continue;
3068 }
3069
3070 /* Allow duplicates for these NUT variables */
3071 if (!strncmp(item->info_type, "ups.alarm", 9) || !strncmp(item->info_type, "ups.status", 10))
3072 break;
3073
3074 /* This one doesn't exist yet */
3075 if (dstate_getinfo(item->info_type) == NULL)
3076 break;
3077
3078 continue;
3079
3080 case QX_WALKMODE_QUICK_UPDATE:
3081
3082 /* Quick update only deals with status and alarms! */
3083 if (!(item->qxflags & QX_FLAG_QUICK_POLL))
3084 continue;
3085
3086 break;
3087
3088 case QX_WALKMODE_FULL_UPDATE:
3089
3090 /* These don't need polling after initinfo() */
3091 if (item->qxflags & (QX_FLAG_ABSENT | QX_FLAG_CMD | QX_FLAG_SETVAR | QX_FLAG_STATIC))
3092 continue;
3093
3094 /* These need to be polled after user changes (setvar / instcmd) */
3095 if ((item->qxflags & QX_FLAG_SEMI_STATIC) && (data_has_changed == FALSE))
3096 continue;
3097
3098 break;
3099
3100 #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT)
3101 # pragma GCC diagnostic push
3102 # pragma GCC diagnostic ignored "-Wcovered-switch-default"
3103 #endif
3104 /* All enum cases defined as of the time of coding
3105 * have been covered above. Handle later definitions,
3106 * memory corruptions and buggy inputs below...
3107 */
3108 default:
3109 fatalx(EXIT_FAILURE, "%s: unknown update mode!", __func__);
3110 #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_COVERED_SWITCH_DEFAULT)
3111 # pragma GCC diagnostic pop
3112 #endif
3113
3114 }
3115
3116 /* Instant commands */
3117 if (item->qxflags & QX_FLAG_CMD) {
3118 dstate_addcmd(item->info_type);
3119 continue;
3120 }
3121
3122 /* Setvars */
3123 if (item->qxflags & QX_FLAG_SETVAR) {
3124
3125 if (item->qxflags & QX_FLAG_NONUT) {
3126 setvar(item->info_type, NULL);
3127 item->qxflags |= QX_FLAG_SKIP;
3128 }
3129
3130 continue;
3131
3132 }
3133
3134 /* Check whether the previous item uses the same command and then use its answer, if available.. */
3135 if (strlen(previous_item.command) > 0 && strlen(previous_item.answer) > 0 && !strcasecmp(previous_item.command, item->command)) {
3136
3137 snprintf(item->answer, sizeof(item->answer), "%s", previous_item.answer);
3138
3139 /* Process the answer */
3140 retcode = qx_process_answer(item, strlen(item->answer));
3141
3142 /* ..otherwise: execute command to get answer from the UPS */
3143 } else {
3144
3145 retcode = qx_process(item, NULL);
3146
3147 }
3148
3149 /* Record item as previous_item */
3150 snprintf(previous_item.command, sizeof(previous_item.command), "%s", item->command);
3151 snprintf(previous_item.answer, sizeof(previous_item.answer), "%s", item->answer);
3152
3153 if (retcode) {
3154
3155 /* Clear data from the item */
3156 memset(item->answer, 0, sizeof(item->answer));
3157 memset(item->value, 0, sizeof(item->value));
3158
3159 if (item->qxflags & QX_FLAG_QUICK_POLL)
3160 return FALSE;
3161
3162 if (mode == QX_WALKMODE_INIT)
3163 /* Skip this item from now on */
3164 item->qxflags |= QX_FLAG_SKIP;
3165
3166 /* Don't know what happened, try again later... */
3167 continue;
3168
3169 }
3170
3171 /* Process the value we got back (set status bits and set the value of other parameters) */
3172 retcode = ups_infoval_set(item);
3173
3174 /* Clear data from the item */
3175 memset(item->answer, 0, sizeof(item->answer));
3176 memset(item->value, 0, sizeof(item->value));
3177
3178 /* Uh-oh! Some error! */
3179 if (retcode == -1) {
3180
3181 if (item->qxflags & QX_FLAG_QUICK_POLL)
3182 return FALSE;
3183
3184 continue;
3185
3186 }
3187
3188 /* Set var flags/range/enum (not for ups.{alarm.status}, hence the retcode check) */
3189 if (retcode && mode == QX_WALKMODE_INIT) {
3190 qx_set_var(item);
3191 }
3192
3193 }
3194
3195 /* Update battery guesstimation */
3196 if (mode == QX_WALKMODE_FULL_UPDATE && (d_equal(batt.runt.act, -1) || d_equal(batt.chrg.act, -1))) {
3197
3198 if (getval("runtimecal")) {
3199
3200 time_t battery_now;
3201
3202 time(&battery_now);
3203
3204 /* OL */
3205 if (ups_status & STATUS(OL)) {
3206
3207 batt.runt.est += batt.runt.nom * difftime(battery_now, battery_lastpoll) / batt.chrg.time;
3208 if (batt.runt.est > batt.runt.nom) {
3209 batt.runt.est = batt.runt.nom;
3210 }
3211
3212 /* OB */
3213 } else {
3214
3215 batt.runt.est -= load.eff * difftime(battery_now, battery_lastpoll);
3216 if (batt.runt.est < 0) {
3217 batt.runt.est = 0;
3218 }
3219
3220 }
3221
3222 if (d_equal(batt.chrg.act, -1))
3223 dstate_setinfo("battery.charge", "%.0f", 100 * batt.runt.est / batt.runt.nom);
3224
3225 if (d_equal(batt.runt.act, -1) && !qx_load())
3226 dstate_setinfo("battery.runtime", "%.0f", batt.runt.est / load.eff);
3227
3228 battery_lastpoll = battery_now;
3229
3230 } else {
3231
3232 qx_battery();
3233
3234 }
3235 }
3236
3237 return TRUE;
3238 }
3239
3240 /* Convert the local status information to NUT format and set NUT alarms. */
ups_alarm_set(void)3241 static void ups_alarm_set(void)
3242 {
3243 if (ups_status & STATUS(RB)) {
3244 alarm_set("Replace battery!");
3245 }
3246 if (ups_status & STATUS(FSD)) {
3247 alarm_set("Shutdown imminent!");
3248 }
3249 }
3250
3251 /* Convert the local status information to NUT format and set NUT status. */
ups_status_set(void)3252 static void ups_status_set(void)
3253 {
3254 if (ups_status & STATUS(OL)) {
3255 status_set("OL"); /* On line */
3256 } else {
3257 status_set("OB"); /* On battery */
3258 }
3259 if (ups_status & STATUS(DISCHRG)) {
3260 status_set("DISCHRG"); /* Discharging */
3261 }
3262 if (ups_status & STATUS(CHRG)) {
3263 status_set("CHRG"); /* Charging */
3264 }
3265 if (ups_status & STATUS(LB)) {
3266 status_set("LB"); /* Low battery */
3267 }
3268 if (ups_status & STATUS(OVER)) {
3269 status_set("OVER"); /* Overload */
3270 }
3271 if (ups_status & STATUS(RB)) {
3272 status_set("RB"); /* Replace battery */
3273 }
3274 if (ups_status & STATUS(TRIM)) {
3275 status_set("TRIM"); /* SmartTrim */
3276 }
3277 if (ups_status & STATUS(BOOST)) {
3278 status_set("BOOST"); /* SmartBoost */
3279 }
3280 if (ups_status & STATUS(BYPASS)) {
3281 status_set("BYPASS"); /* On bypass */
3282 }
3283 if (ups_status & STATUS(OFF)) {
3284 status_set("OFF"); /* UPS is off */
3285 }
3286 if (ups_status & STATUS(CAL)) {
3287 status_set("CAL"); /* Calibration */
3288 }
3289 if (ups_status & STATUS(FSD)) {
3290 status_set("FSD"); /* Forced shutdown */
3291 }
3292 }
3293
3294 /* See header file for details. */
find_nut_info(const char * varname,const unsigned long flag,const unsigned long noflag)3295 item_t *find_nut_info(const char *varname, const unsigned long flag, const unsigned long noflag)
3296 {
3297 item_t *item;
3298
3299 for (item = subdriver->qx2nut; item->info_type != NULL; item++) {
3300
3301 if (strcasecmp(item->info_type, varname))
3302 continue;
3303
3304 if (flag && ((item->qxflags & flag) != flag))
3305 continue;
3306
3307 if (noflag && (item->qxflags & noflag))
3308 continue;
3309
3310 return item;
3311 }
3312
3313 upsdebugx(2, "%s: info type %s not found", __func__, varname);
3314 return NULL;
3315 }
3316
3317 /* Process the answer we got back from the UPS
3318 * Return -1 on errors, 0 on success */
qx_process_answer(item_t * item,const size_t len)3319 static int qx_process_answer(item_t *item, const size_t len)
3320 {
3321 /* Query rejected by the UPS */
3322 if (subdriver->rejected && !strcasecmp(item->answer, subdriver->rejected)) {
3323 upsdebugx(2, "%s: query rejected by the UPS (%s)", __func__, item->info_type);
3324 return -1;
3325 }
3326
3327 /* Short reply */
3328 if (item->answer_len && len < item->answer_len) {
3329 upsdebugx(2, "%s: short reply (%s)", __func__, item->info_type);
3330 return -1;
3331 }
3332
3333 /* Wrong leading character */
3334 if (item->leading && item->answer[0] != item->leading) {
3335 upsdebugx(2, "%s: %s - invalid start character [%02x], expected [%02x]", __func__, item->info_type, item->answer[0], item->leading);
3336 return -1;
3337 }
3338
3339 /* Check boundaries */
3340 if (item->to && item->to < item->from) {
3341 upsdebugx(1, "%s: in %s, starting char's position (%d) follows ending char's one (%d)", __func__, item->info_type, item->from, item->to);
3342 return -1;
3343 }
3344
3345 /* Get value */
3346 if (strlen(item->answer)) {
3347 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);
3348 } else {
3349 snprintf(item->value, sizeof(item->value), "%s", "");
3350 }
3351
3352 return 0;
3353 }
3354
3355 /* See header file for details. */
qx_process(item_t * item,const char * command)3356 int qx_process(item_t *item, const char *command)
3357 {
3358 char buf[sizeof(item->answer) - 1] = "", *cmd;
3359 ssize_t len;
3360 size_t cmdlen = command ?
3361 (strlen(command) >= SMALLBUF ? strlen(command) + 1 : SMALLBUF) :
3362 (item->command && strlen(item->command) >= SMALLBUF ? strlen(item->command) + 1 : SMALLBUF);
3363 size_t cmdsz = (sizeof(char) * cmdlen); /* in bytes, to be pedantic */
3364
3365 if ( !(cmd = xmalloc(cmdsz)) ) {
3366 upslogx(LOG_ERR, "qx_process() failed to allocate buffer");
3367 return -1;
3368 }
3369
3370 /* Prepare the command to be used */
3371 memset(cmd, 0, cmdsz);
3372 snprintf(cmd, cmdsz, "%s", command ? command : item->command);
3373
3374 /* Preprocess the command */
3375 if (
3376 item->preprocess_command != NULL &&
3377 item->preprocess_command(item, cmd, cmdsz) == -1
3378 ) {
3379 upsdebugx(4, "%s: failed to preprocess command [%s]", __func__, item->info_type);
3380 free (cmd);
3381 return -1;
3382 }
3383
3384 /* Send the command */
3385 len = qx_command(cmd, buf, sizeof(buf));
3386
3387 memset(item->answer, 0, sizeof(item->answer));
3388
3389 if (len < 0 || len > INT_MAX) {
3390 upsdebugx(4, "%s: failed to preprocess answer [%s]", __func__, item->info_type);
3391 free (cmd);
3392 return -1;
3393 }
3394
3395 memcpy(item->answer, buf, sizeof(buf));
3396
3397 /* Preprocess the answer */
3398 if (item->preprocess_answer != NULL) {
3399 len = item->preprocess_answer(item, (int)len);
3400 if (len < 0 || len > INT_MAX) {
3401 upsdebugx(4, "%s: failed to preprocess answer [%s]", __func__, item->info_type);
3402 /* Clear answer, preventing it from being reused by next items with same command */
3403 memset(item->answer, 0, sizeof(item->answer));
3404 free (cmd);
3405 return -1;
3406 }
3407 }
3408
3409 free (cmd);
3410
3411 /* Process the answer to get the value */
3412 return qx_process_answer(item, (size_t)len);
3413 }
3414
3415 /* See header file for details. */
ups_infoval_set(item_t * item)3416 int ups_infoval_set(item_t *item)
3417 {
3418 char value[SMALLBUF] = "";
3419
3420 /* Item need to be preprocessed? */
3421 if (item->preprocess != NULL){
3422
3423 /* Process the value returned by the UPS to NUT standards */
3424 if (item->preprocess(item, value, sizeof(value))) {
3425 upsdebugx(4, "%s: failed to preprocess value [%s: %s]", __func__, item->info_type, item->value);
3426 return -1;
3427 }
3428
3429 /* Deal with status items */
3430 if (!strncmp(item->info_type, "ups.status", 10)) {
3431 if (strlen(value) > 0)
3432 update_status(value);
3433 return 0;
3434 }
3435
3436 /* Deal with alarm items */
3437 if (!strncmp(item->info_type, "ups.alarm", 9)) {
3438 if (strlen(value) > 0)
3439 alarm_set(value);
3440 return 0;
3441 }
3442
3443 } else {
3444
3445 snprintf(value, sizeof(value), "%s", item->value);
3446
3447 /* Cover most of the cases: either left/right filled with hashes, spaces or a mix of both */
3448 if (item->qxflags & QX_FLAG_TRIM)
3449 str_trim_m(value, "# ");
3450
3451 if (strncasecmp(item->dfl, "%s", 2)) {
3452
3453 if (strspn(value, "0123456789 .") != strlen(value)) {
3454 upsdebugx(2, "%s: non numerical value [%s: %s]", __func__, item->info_type, value);
3455 return -1;
3456 }
3457
3458 #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
3459 #pragma GCC diagnostic push
3460 #endif
3461 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
3462 #pragma GCC diagnostic ignored "-Wformat-nonliteral"
3463 #endif
3464 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_FORMAT_SECURITY
3465 #pragma GCC diagnostic ignored "-Wformat-security"
3466 #endif
3467 snprintf(value, sizeof(value), item->dfl, strtod(value, NULL));
3468 #ifdef HAVE_PRAGMAS_FOR_GCC_DIAGNOSTIC_IGNORED_FORMAT_NONLITERAL
3469 #pragma GCC diagnostic pop
3470 #endif
3471 }
3472
3473 }
3474
3475 if (item->qxflags & QX_FLAG_NONUT) {
3476 upslogx(LOG_INFO, "%s: %s", item->info_type, value);
3477 return 1;
3478 }
3479
3480 if (!strlen(value)) {
3481 upsdebugx(1, "%s: non significant value [%s]", __func__, item->info_type);
3482 return -1;
3483 }
3484
3485 dstate_setinfo(item->info_type, "%s", value);
3486
3487 /* Fill batt.{chrg,runt}.act for guesstimation */
3488 if (!strcasecmp(item->info_type, "battery.charge"))
3489 batt.chrg.act = strtol(value, NULL, 10);
3490 else if (!strcasecmp(item->info_type, "battery.runtime"))
3491 batt.runt.act = strtol(value, NULL, 10);
3492
3493 return 1;
3494 }
3495
3496 /* See header file for details. */
qx_status(void)3497 unsigned int qx_status(void)
3498 {
3499 return ups_status;
3500 }
3501