nut/drivers/liebert.c
2022-06-29 12:37:36 +02:00

196 lines
4 KiB
C

/* liebert.c - support for Liebert UPS models via MultiLink cable.
Copyright (C) 2002 Russell Kroll <rkroll@exploits.org>
Based on old-style multilink.c driver:
Copyright (C) 2001 Rick Lyons <rick@powerup.com.au>
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 <rkroll@exploits.org>\n" \
"Rick Lyons <rick@powerup.com.au>",
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);
}