/* liebert.c - support for Liebert UPS models via MultiLink cable. Copyright (C) 2002 Russell Kroll Based on old-style multilink.c driver: Copyright (C) 2001 Rick Lyons This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "config.h" #include "main.h" #include "serial.h" #include "attribute.h" #define DRIVER_NAME "Liebert MultiLink UPS driver" #define DRIVER_VERSION "1.03" /* driver description structure */ upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, "Russell Kroll \n" \ "Rick Lyons ", DRV_EXPERIMENTAL, { NULL } }; #define ML_ONBATTERY 0x55 void upsdrv_shutdown(void) __attribute__((noreturn)); void upsdrv_shutdown(void) { /* XXX: replace with a proper shutdown function (raise DTR) */ /* worse yet: stock cables don't support shutdown at all */ fatalx(EXIT_FAILURE, "shutdown not supported"); } void upsdrv_initinfo(void) { char *tmp; tmp = getval("mfr"); if (!tmp) dstate_setinfo("ups.mfr", "Liebert"); else dstate_setinfo("ups.mfr", "%s", tmp); tmp = getval("model"); if (!tmp) dstate_setinfo("ups.model", "MultiLink"); else dstate_setinfo("ups.model", "%s", tmp); } /* require this many OBs or LBs before actually setting it */ #define DEBOUNCE 3 /* normal idle loop - keep up with the current state of the UPS */ void upsdrv_updateinfo(void) { unsigned char c; unsigned int ob, lb; static unsigned int ob_state = 0, ob_last = 0, ob_ctr = 0; static unsigned int lb_state = 0, lb_last = 0, lb_ctr = 0; ob = lb = 0; /* the UPS connects RX to TX when on battery, so test for loopback */ ser_flush_in(upsfd, "", 0); c = ML_ONBATTERY; ser_send_char(upsfd, c); if (ser_get_char(upsfd, &c, 1, 0) == 1) { while (ser_get_char(upsfd, &c, 1, 0) == 1) continue; if (c == ML_ONBATTERY) ob = 1; } if (ser_get_dcd(upsfd)) lb = 1; /* state machine below to ensure status changes are debounced */ /* OB/OL state change: reset counter */ if (ob_last != ob) ob_ctr = 0; else ob_ctr++; upsdebugx(2, "OB: state %d last %d now %d ctr %d", ob_state, ob_last, ob, ob_ctr); if (ob_ctr >= DEBOUNCE) { if (ob != ob_state) { upsdebugx(2, "OB: toggling state"); if (ob_state == 0) ob_state = 1; else ob_state = 0; } } ob_last = ob; /* now do it again for LB */ /* state change: reset counter */ if (lb_last != lb) lb_ctr = 0; else lb_ctr++; upsdebugx(2, "LB: state %d last %d now %d ctr %d", lb_state, lb_last, lb, lb_ctr); if (lb_ctr >= DEBOUNCE) { if (lb != lb_state) { upsdebugx(2, "LB: toggling state"); if (lb_state == 0) lb_state = 1; else lb_state = 0; } } lb_last = lb; status_init(); if (ob_state == 1) status_set("OB"); /* on battery */ else status_set("OL"); /* on line */ if (lb_state == 1) status_set("LB"); /* low battery */ status_commit(); dstate_dataok(); } void upsdrv_makevartable(void) { addvar(VAR_VALUE, "mfr", "Override manufacturer name"); addvar(VAR_VALUE, "model", "Override model name"); } void upsdrv_help(void) { } void upsdrv_initups(void) { upsfd = ser_open(device_path); /* Speed should not matter (see comments in upsdrv_updateinfo), * but set it relatively low in case there are problems with higher * speeds. */ ser_set_speed(upsfd, device_path, B9600); /* raise RTS */ ser_set_rts(upsfd, 1); } void upsdrv_cleanup(void) { ser_close(upsfd, device_path); }