nut-debian/drivers/riello_usb.c
2014-04-22 20:39:47 +02:00

1067 lines
26 KiB
C

/*
* riello_usb.c: support for Riello USB protocol based UPSes
*
* A document describing the protocol implemented by this driver can be
* found online at:
*
* http://www.networkupstools.org/ups-protocols/riello/PSGPSER-0104.pdf
*
* Copyright (C) 2012 - Elio Parisi <e.parisi@riello-ups.com>
*
* 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
*
* Reference of the derivative work: blazer driver
*/
#include <stdint.h>
#include "main.h"
#include "libusb.h"
#include "usb-common.h"
#include "riello.h"
#define DRIVER_NAME "Riello USB driver"
#define DRIVER_VERSION "0.02"
/* driver description structure */
upsdrv_info_t upsdrv_info = {
DRIVER_NAME,
DRIVER_VERSION,
"Elio Parisi <e.parisi@riello-ups.com>",
DRV_EXPERIMENTAL,
{ NULL }
};
uint8_t bufOut[BUFFER_SIZE];
uint8_t bufIn[BUFFER_SIZE];
uint8_t gpser_error_control;
uint8_t input_monophase;
uint8_t output_monophase;
extern uint8_t commbyte;
extern uint8_t wait_packet;
extern uint8_t foundnak;
extern uint8_t foundbadcrc;
extern uint8_t buf_ptr_length;
extern uint8_t requestSENTR;
TRielloData DevData;
static usb_communication_subdriver_t *usb = &usb_subdriver;
static usb_dev_handle *udev = NULL;
static USBDevice_t usbdevice;
static USBDeviceMatcher_t *reopen_matcher = NULL;
static USBDeviceMatcher_t *regex_matcher = NULL;
static int (*subdriver_command)(uint8_t *cmd, uint8_t *buf, uint16_t length, uint16_t buflen) = NULL;
void ussleep(long usec)
{
if (usec == 1)
usec = 400;
else
usec *= 1000;
usleep(usec);
}
static int cypress_setfeatures()
{
int ret;
bufOut[0] = 0xB0;
bufOut[1] = 0x4;
bufOut[2] = 0x0;
bufOut[3] = 0x0;
bufOut[4] = 0x3;
/* Write features report */
ret = usb_control_msg(udev, USB_ENDPOINT_OUT + USB_TYPE_CLASS + USB_RECIP_INTERFACE,
0x09, /* HID_REPORT_SET = 0x09 */
0 + (0x03 << 8), /* HID_REPORT_TYPE_FEATURE */
0, (char*) bufOut, 0x5, 1000);
if (ret <= 0) {
upsdebugx(3, "send: %s", ret ? usb_strerror() : "error");
return ret;
}
upsdebugx(3, "send: features report ok");
return ret;
}
uint8_t Send_USB_Packet(uint8_t *send_str, uint16_t numbytes)
{
uint8_t USB_buff_pom[10];
int i, err, size, errno;
/* is input correct ? */
if ((!send_str) || (!numbytes))
return 1;
size = 7;
/* routine which parse report into 4-bytes packet */
for (i=0; i<(numbytes/size); i++) {
USB_buff_pom[0] = 0x37;
USB_buff_pom[1] = send_str[(i*7)];
USB_buff_pom[2] = send_str[(i*7)+1];
USB_buff_pom[3] = send_str[(i*7)+2];
USB_buff_pom[4] = send_str[(i*7)+3];
USB_buff_pom[5] = send_str[(i*7)+4];
USB_buff_pom[6] = send_str[(i*7)+5];
USB_buff_pom[7] = send_str[(i*7)+6];
err = usb_bulk_write(udev, 0x2, (char*) USB_buff_pom, 8, 1000);
if (err < 0) {
if (err==-13 || errno==19)
upsdebugx(3, "USB device disconnected !");
else
upsdebugx(3, "USB: Send_USB_Packet: send_usb_packet, err = %08x %s ", err, strerror(errno));
return 2;
}
ussleep(USB_WRITE_DELAY);
}
/* send rest of packet */
if (numbytes % size) {
i = numbytes/size;
memset(USB_buff_pom, '0', sizeof(USB_buff_pom));
USB_buff_pom[0] = 0x30+(numbytes%7);
if ((i*7)<numbytes)
USB_buff_pom[1] = send_str[(i*7)];
if (((i*7)+1)<numbytes)
USB_buff_pom[2] = send_str[(i*7)+1];
if (((i*7)+2)<numbytes)
USB_buff_pom[3] = send_str[(i*7)+2];
if (((i*7)+3)<numbytes)
USB_buff_pom[4] = send_str[(i*7)+3];
if (((i*7)+4)<numbytes)
USB_buff_pom[5] = send_str[(i*7)+4];
if (((i*7)+5)<numbytes)
USB_buff_pom[6] = send_str[(i*7)+5];
if (((i*7)+6)<numbytes)
USB_buff_pom[7] = send_str[(i*7)+6];
err = usb_bulk_write(udev, 0x2, (char*) USB_buff_pom, 8, 1000);
if (err < 0) {
if (err==-13 || errno==19)
upsdebugx(3, "USB device disconnected !");
else
upsdebugx(3, "USB: Send_USB_Packet: send_usb_packet, err = %08x %s ", err, strerror(errno));
return 2;
}
ussleep(USB_WRITE_DELAY);
}
return (0);
}
uint8_t Get_USB_Packet(uint8_t *buffer)
{
char inBuf[10];
int err, size, errno, ep;
/* note: this function stop until some byte(s) is not arrived */
size = 8;
ep = 0x81 | USB_ENDPOINT_IN;
err = usb_bulk_read(udev, ep, (char*) inBuf, size, 1000);
upsdebugx(3, "read: %02X %02X %02X %02X %02X %02X %02X %02X", inBuf[0], inBuf[1], inBuf[2], inBuf[3], inBuf[4], inBuf[5], inBuf[6], inBuf[7]);
if (err == 0) {
if (err==-13 || err==-19 || errno==19)
upsdebugx(3, "USB device disconnected !");
else {
switch (errno) {
case 11: /* resource temp. not available */
case 16: /* device busy */
/* ignore it */
break;
default:
upsdebugx(3, "USB: Get_USB_Packet: send_usb_packet, err = %08x %s ", err, strerror(errno));
break;
}
}
return (0);
}
/* copy to buffer */
size = inBuf[0] & 0x07;
if (size)
memcpy(buffer, &inBuf[1], size);
return(size);
}
static int cypress_command(uint8_t *buffer, uint8_t *buf, uint16_t length, uint16_t buflen)
{
int ret, i = 0;
uint8_t USB_buff[BUFFER_SIZE];
/* read to flush buffer */
/* ret = Get_USB_Packet(buf+i);*/
riello_init_serial();
/* send packet */
ret = Send_USB_Packet(buffer, length);
if (ret > 0) {
upsdebugx(3, "send: %s", ret ? usb_strerror() : "timeout");
return ret;
}
upsdebugx(3, "send ok");
memset(buf, 0, buflen);
while ((buf_ptr_length < BUFFER_SIZE) && wait_packet) {
memset(USB_buff, 0, sizeof(USB_buff));
ret = Get_USB_Packet(USB_buff);
/*
* Any errors here mean that we are unable to read a reply (which
* will happen after successfully writing a command to the UPS)
*/
if (ret < 0) {
upsdebugx(3, "read: %s", ret ? usb_strerror() : "timeout");
return ret;
}
for (i = 0; i < ret; i++ ) {
commbyte = USB_buff[i];
riello_parse_serialport(DEV_RIELLOGPSER, buf, gpser_error_control);
}
}
upsdebugx(3, "in read: %u", buf_ptr_length);
return buf_ptr_length;
}
static void *cypress_subdriver(USBDevice_t *device)
{
subdriver_command = &cypress_command;
return NULL;
}
/* Riello (Cypress Semiconductor Corp.) */
#define RIELLO_VENDORID 0x04b4
static usb_device_id_t riello_usb_id[] = {
/* various models */
{ USB_DEVICE(RIELLO_VENDORID, 0x5500), &cypress_subdriver },
/* end of list */
{-1, -1, NULL}
};
static int device_match_func(USBDevice_t *hd, void *privdata)
{
if (subdriver_command) {
return 1;
}
switch (is_usb_device_supported(riello_usb_id, hd))
{
case SUPPORTED:
return 1;
case POSSIBLY_SUPPORTED:
case NOT_SUPPORTED:
default:
return 0;
}
}
static USBDeviceMatcher_t device_matcher = {
&device_match_func,
NULL,
NULL
};
/*
* Generic command processing function. Send a command and read a reply.
* Returns < 0 on error, 0 on timeout and the number of bytes read on
* success.
*/
int riello_command(uint8_t *cmd, uint8_t *buf, uint16_t length, uint16_t buflen)
{
int ret;
if (udev == NULL) {
ret = usb->open(&udev, &usbdevice, reopen_matcher, NULL);
if (ret < 1) {
return ret;
}
}
ret = (*subdriver_command)(cmd, buf, length, buflen);
if (ret >= 0) {
return ret;
}
switch (ret)
{
case -EBUSY: /* Device or resource busy */
fatal_with_errno(EXIT_FAILURE, "Got disconnected by another driver");
case -EPERM: /* Operation not permitted */
fatal_with_errno(EXIT_FAILURE, "Permissions problem");
case -EPIPE: /* Broken pipe */
if (usb_clear_halt(udev, 0x81) == 0) {
upsdebugx(1, "Stall condition cleared");
break;
}
#ifdef ETIME
case -ETIME: /* Timer expired */
#endif
if (usb_reset(udev) == 0) {
upsdebugx(1, "Device reset handled");
}
case -ENODEV: /* No such device */
case -EACCES: /* Permission denied */
case -EIO: /* I/O error */
case -ENXIO: /* No such device or address */
case -ENOENT: /* No such file or directory */
/* Uh oh, got to reconnect! */
usb->close(udev);
udev = NULL;
break;
case -ETIMEDOUT: /* Connection timed out */
case -EOVERFLOW: /* Value too large for defined data type */
#ifdef EPROTO
case -EPROTO: /* Protocol error */
#endif
default:
break;
}
return ret;
}
int get_ups_nominal()
{
uint8_t recv, length;
length = riello_prepare_gn(&bufOut[0], gpser_error_control);
recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_GN);
if (!wait_packet && foundbadcrc) {
upsdebugx (3, "Get nominal Ko: bad CRC or Checksum");
return 1;
}
if (!wait_packet && foundnak) {
upsdebugx (3, "Get nominal Ko: command not supported");
return 1;
}
upsdebugx (3, "Get nominal Ok: read byte: %u", recv);
riello_parse_gn(&bufIn[0], &DevData);
return 0;
}
int get_ups_status()
{
uint8_t recv, numread, length;
length = riello_prepare_rs(&bufOut[0], gpser_error_control);
if (input_monophase)
numread = LENGTH_RS_MM;
else if (output_monophase)
numread = LENGTH_RS_TM;
else
numread = LENGTH_RS_TT;
recv = riello_command(&bufOut[0], &bufIn[0], length, numread);
if (!wait_packet && foundbadcrc) {
upsdebugx (3, "Get status Ko: bad CRC or Checksum");
return 1;
}
if (!wait_packet && foundnak) {
upsdebugx (3, "Get status Ko: command not supported");
return 1;
}
upsdebugx (3, "Get status Ok: read byte: %u", recv);
riello_parse_rs(&bufIn[0], &DevData, numread);
return 0;
}
int get_ups_extended()
{
uint8_t recv, length;
length = riello_prepare_re(&bufOut[0], gpser_error_control);
recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_RE);
if (!wait_packet && foundbadcrc) {
upsdebugx (3, "Get extended Ko: bad CRC or Checksum");
return 1;
}
if (!wait_packet && foundnak) {
upsdebugx (3, "Get extended Ko: command not supported");
return 1;
}
upsdebugx (3, "Get extended Ok: read byte: %u", recv);
riello_parse_re(&bufIn[0], &DevData);
return 0;
}
int get_ups_statuscode()
{
uint8_t recv, length;
length = riello_prepare_rc(&bufOut[0], gpser_error_control);
recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_RC);
if (!wait_packet && foundbadcrc) {
upsdebugx (3, "Get statuscode Ko: bad CRC or Checksum");
return 1;
}
if (!wait_packet && foundnak) {
upsdebugx (3, "Get statuscode Ko: command not supported");
return 1;
}
upsdebugx (3, "Get statuscode Ok: read byte: %u", recv);
riello_parse_rc(&bufIn[0], &DevData);
return 0;
}
int riello_instcmd(const char *cmdname, const char *extra)
{
uint8_t length, recv;
uint16_t delay;
const char *delay_char;
if (!riello_test_bit(&DevData.StatusCode[0], 1)) {
if (!strcasecmp(cmdname, "load.off")) {
delay = 0;
length = riello_prepare_cs(bufOut, gpser_error_control, delay);
recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_DEF);
if (recv > 0) {
if (!wait_packet && foundbadcrc) {
upsdebugx (3, "Command load.off Ko: bad CRC or Checksum");
return STAT_INSTCMD_FAILED;
}
if (!wait_packet && foundnak) {
upsdebugx (3, "Command load.off Ko: command not supported");
return STAT_INSTCMD_FAILED;
}
return STAT_INSTCMD_HANDLED;
}
else
return STAT_INSTCMD_FAILED;
}
if (!strcasecmp(cmdname, "load.off.delay")) {
delay_char = dstate_getinfo("ups.delay.shutdown");
delay = atoi(delay_char);
length = riello_prepare_cs(bufOut, gpser_error_control, delay);
recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_DEF);
if (recv > 0) {
if (!wait_packet && foundbadcrc) {
upsdebugx (3, "Command load.off.delay Ko: bad CRC or Checksum");
return STAT_INSTCMD_FAILED;
}
if (!wait_packet && foundnak) {
upsdebugx (3, "Command load.off.delay Ko: command not supported");
return STAT_INSTCMD_FAILED;
}
return STAT_INSTCMD_HANDLED;
}
else
return STAT_INSTCMD_FAILED;
}
if (!strcasecmp(cmdname, "load.on")) {
delay = 0;
length = riello_prepare_cr(bufOut, gpser_error_control, delay);
recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_DEF);
if (recv > 0) {
if (!wait_packet && foundbadcrc) {
upsdebugx (3, "Command load.on Ko: bad CRC or Checksum");
return STAT_INSTCMD_FAILED;
}
if (!wait_packet && foundnak) {
upsdebugx (3, "Command load.on Ko: command not supported");
return STAT_INSTCMD_FAILED;
}
return STAT_INSTCMD_HANDLED;
}
else
return STAT_INSTCMD_FAILED;
}
if (!strcasecmp(cmdname, "load.on.delay")) {
delay_char = dstate_getinfo("ups.delay.reboot");
delay = atoi(delay_char);
length = riello_prepare_cr(bufOut, gpser_error_control, delay);
recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_DEF);
if (recv > 0) {
if (!wait_packet && foundbadcrc) {
upsdebugx (3, "Command load.on.delay Ko: bad CRC or Checksum");
return STAT_INSTCMD_FAILED;
}
if (!wait_packet && foundnak) {
upsdebugx (3, "Command load.on.delay Ko: command not supported");
return STAT_INSTCMD_FAILED;
}
return STAT_INSTCMD_HANDLED;
}
else
return STAT_INSTCMD_FAILED;
}
}
else {
if (!strcasecmp(cmdname, "shutdown.return")) {
delay_char = dstate_getinfo("ups.delay.shutdown");
delay = atoi(delay_char);
length = riello_prepare_cs(bufOut, gpser_error_control, delay);
recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_DEF);
if (recv > 0) {
if (!wait_packet && foundbadcrc) {
upsdebugx (3, "Command shutdown.return Ko: bad CRC or Checksum");
return STAT_INSTCMD_FAILED;
}
if (!wait_packet && foundnak) {
upsdebugx (3, "Command shutdown.return Ko: command not supported");
return STAT_INSTCMD_FAILED;
}
return STAT_INSTCMD_HANDLED;
}
else
return STAT_INSTCMD_FAILED;
}
}
if (!strcasecmp(cmdname, "shutdown.stop")) {
length = riello_prepare_cd(bufOut, gpser_error_control);
recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_DEF);
if (recv > 0) {
if (!wait_packet && foundbadcrc) {
upsdebugx (3, "Command shutdown.stop Ko: bad CRC or Checksum");
return STAT_INSTCMD_FAILED;
}
if (!wait_packet && foundnak) {
upsdebugx (3, "Command shutdown.stop Ko: command not supported");
return STAT_INSTCMD_FAILED;
}
return STAT_INSTCMD_HANDLED;
}
else
return STAT_INSTCMD_FAILED;
}
if (!strcasecmp(cmdname, "test.panel.start")) {
length = riello_prepare_tp(bufOut, gpser_error_control);
recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_DEF);
if (recv > 0) {
if (!wait_packet && foundbadcrc) {
upsdebugx (3, "Command test.panel.start Ko: bad CRC or Checksum");
return STAT_INSTCMD_FAILED;
}
if (!wait_packet && foundnak) {
upsdebugx (3, "Command test.panel.start Ko: command not supported");
return STAT_INSTCMD_FAILED;
}
return STAT_INSTCMD_HANDLED;
}
else
return STAT_INSTCMD_FAILED;
}
if (!strcasecmp(cmdname, "test.battery.start")) {
length = riello_prepare_tb(bufOut, gpser_error_control);
recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_DEF);
if (recv > 0) {
if (!wait_packet && foundbadcrc) {
upsdebugx (3, "Command test.battery.start Ko: bad CRC or Checksum");
return STAT_INSTCMD_FAILED;
}
if (!wait_packet && foundnak) {
upsdebugx (3, "Command test.battery.start Ko: command not supported");
return STAT_INSTCMD_FAILED;
}
return STAT_INSTCMD_HANDLED;
}
else
return STAT_INSTCMD_FAILED;
}
upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname);
return STAT_INSTCMD_UNKNOWN;
}
int start_ups_comm()
{
uint16_t length;
int recv;
upsdebugx (2, "entering start_ups_comm()\n");
cypress_setfeatures();
length = riello_prepare_gi(&bufOut[0]);
recv = riello_command(&bufOut[0], &bufIn[0], length, LENGTH_GI);
if (!wait_packet && foundbadcrc) {
upsdebugx (3, "Get identif Ko: bad CRC or Checksum");
return 1;
}
if (!wait_packet && foundnak) {
upsdebugx (3, "Get identif Ko: command not supported");
return 1;
}
upsdebugx (3, "Get identif Ok: read byte: %u", recv);
return 0;
}
void upsdrv_help(void)
{
}
void upsdrv_makevartable(void)
{
}
void upsdrv_initups(void)
{
const struct {
const char *name;
int (*command)(uint8_t *cmd, uint8_t *buf, uint16_t length, uint16_t buflen);
} subdriver[] = {
{ "cypress", &cypress_command },
{ NULL }
};
int ret;
char *regex_array[6];
char *subdrv = getval("subdriver");
regex_array[0] = getval("vendorid");
regex_array[1] = getval("productid");
regex_array[2] = getval("vendor");
regex_array[3] = getval("product");
regex_array[4] = getval("serial");
regex_array[5] = getval("bus");
/* pick up the subdriver name if set explicitly */
if (subdrv) {
int i;
if (!regex_array[0] || !regex_array[1]) {
fatalx(EXIT_FAILURE, "When specifying a subdriver, 'vendorid' and 'productid' are mandatory.");
}
for (i = 0; subdriver[i].name; i++) {
if (strcasecmp(subdrv, subdriver[i].name)) {
continue;
}
subdriver_command = subdriver[i].command;
break;
}
if (!subdriver_command) {
fatalx(EXIT_FAILURE, "Subdriver \"%s\" not found!", subdrv);
}
}
ret = USBNewRegexMatcher(&regex_matcher, regex_array, REG_ICASE | REG_EXTENDED);
switch (ret)
{
case -1:
fatal_with_errno(EXIT_FAILURE, "USBNewRegexMatcher");
case 0:
break; /* all is well */
default:
fatalx(EXIT_FAILURE, "invalid regular expression: %s", regex_array[ret]);
}
/* link the matchers */
regex_matcher->next = &device_matcher;
ret = usb->open(&udev, &usbdevice, regex_matcher, NULL);
if (ret < 0) {
fatalx(EXIT_FAILURE,
"No supported devices found. Please check your device availability with 'lsusb'\n"
"and make sure you have an up-to-date version of NUT. If this does not help,\n"
"try running the driver with at least 'subdriver', 'vendorid' and 'productid'\n"
"options specified. Please refer to the man page for details about these options\n"
"(man 8 riello_usb).\n");
}
if (!subdriver_command) {
fatalx(EXIT_FAILURE, "No subdriver selected");
}
/* create a new matcher for later reopening */
ret = USBNewExactMatcher(&reopen_matcher, &usbdevice);
if (ret) {
fatal_with_errno(EXIT_FAILURE, "USBNewExactMatcher");
}
/* link the matchers */
reopen_matcher->next = regex_matcher;
dstate_setinfo("ups.vendorid", "%04x", usbdevice.VendorID);
dstate_setinfo("ups.productid", "%04x", usbdevice.ProductID);
}
void upsdrv_initinfo(void)
{
int ret;
ret = start_ups_comm();
if (ret < 0)
fatalx(EXIT_FAILURE, "No communication with UPS");
else if (ret > 0)
fatalx(EXIT_FAILURE, "Bad checksum or NACK");
else
upsdebugx(2, "Communication with UPS established");
riello_parse_gi(&bufIn[0], &DevData);
gpser_error_control = DevData.Identif_bytes[4]-0x30;
if ((DevData.Identif_bytes[0] == '1') || (DevData.Identif_bytes[0] == '2'))
input_monophase = 1;
else {
input_monophase = 0;
dstate_setinfo("input.phases", "%u", 3);
dstate_setinfo("input.phases", "%u", 3);
dstate_setinfo("input.bypass.phases", "%u", 3);
}
if ((DevData.Identif_bytes[0] == '1') || (DevData.Identif_bytes[0] == '3'))
output_monophase = 1;
else {
output_monophase = 0;
dstate_setinfo("output.phases", "%u", 3);
}
dstate_setinfo("device.mfr", "RPS S.p.a.");
dstate_setinfo("device.model", "%s", (unsigned char*) DevData.ModelStr);
dstate_setinfo("device.serial", "%s", (unsigned char*) DevData.Identification);
dstate_setinfo("device.type", "ups");
dstate_setinfo("ups.mfr", "RPS S.p.a.");
dstate_setinfo("ups.model", "%s", (unsigned char*) DevData.ModelStr);
dstate_setinfo("ups.serial", "%s", (unsigned char*) DevData.Identification);
dstate_setinfo("ups.firmware", "%s", (unsigned char*) DevData.Version);
if (get_ups_nominal() == 0) {
dstate_setinfo("ups.realpower.nominal", "%u", DevData.NomPowerKW);
dstate_setinfo("ups.power.nominal", "%u", DevData.NomPowerKVA);
dstate_setinfo("output.voltage.nominal", "%u", DevData.NominalUout);
dstate_setinfo("output.frequency.nominal", "%.1f", DevData.NomFout/10.0);
dstate_setinfo("battery.voltage.nominal", "%u", DevData.NomUbat);
dstate_setinfo("battery.capacity", "%u", DevData.NomBatCap);
}
/* commands ----------------------------------------------- */
dstate_addcmd("load.off");
dstate_addcmd("load.on");
dstate_addcmd("load.off.delay");
dstate_addcmd("load.on.delay");
dstate_addcmd("shutdown.return");
dstate_addcmd("shutdown.stop");
dstate_addcmd("test.battery.start");
dstate_addcmd("test.panel.start");
/* install handlers */
/* upsh.setvar = hid_set_value; setvar; */
/* note: for a transition period, these data are redundant! */
/* dstate_setinfo("device.mfr", "skel manufacturer"); */
/* dstate_setinfo("device.model", "longrun 15000"); */
upsh.instcmd = riello_instcmd;
}
void upsdrv_shutdown(void)
{
/* tell the UPS to shut down, then return - DO NOT SLEEP HERE */
int retry;
/* maybe try to detect the UPS here, but try a shutdown even if
it doesn't respond at first if possible */
/* replace with a proper shutdown function */
/* you may have to check the line status since the commands
for toggling power are frequently different for OL vs. OB */
/* OL: this must power cycle the load if possible */
/* OB: the load must remain off until the power returns */
for (retry = 1; retry <= MAXTRIES; retry++) {
if (riello_instcmd("shutdown.stop", NULL) != STAT_INSTCMD_HANDLED) {
continue;
}
if (riello_instcmd("shutdown.return", NULL) != STAT_INSTCMD_HANDLED) {
continue;
}
fatalx(EXIT_SUCCESS, "Shutting down");
}
fatalx(EXIT_FAILURE, "Shutdown failed!");
}
void upsdrv_updateinfo(void)
{
uint8_t getextendedOK;
static int countlost = 0;
if (countlost < COUNTLOST)
upsdebugx(1, "Communication with UPS is lost: status read failed!");
else if (countlost == COUNTLOST)
upslogx(LOG_WARNING, "Communication with UPS is lost: status read failed!");
else
dstate_datastale();
if (get_ups_status() != 0) {
if (countlost <= COUNTLOST)
countlost++;
return;
}
if (get_ups_extended() == 0)
getextendedOK = 1;
else
getextendedOK = 0;
if (countlost > COUNTLOST)
upslogx(LOG_NOTICE, "Communication with UPS is re-established!");
dstate_setinfo("input.frequency", "%.2f", DevData.Finp/10.0);
dstate_setinfo("input.bypass.frequency", "%.2f", DevData.Fbypass/10.0);
dstate_setinfo("output.frequency", "%.2f", DevData.Fout/10.0);
dstate_setinfo("battery.voltage", "%.1f", DevData.Ubat/10.0);
dstate_setinfo("battery.charge", "%u", DevData.BatCap);
dstate_setinfo("battery.runtime", "%u", DevData.BatTime*60);
dstate_setinfo("ups.temperature", "%u", DevData.Tsystem);
if (input_monophase) {
dstate_setinfo("input.voltage", "%u", DevData.Uinp1);
dstate_setinfo("input.bypass.voltage", "%u", DevData.Ubypass1);
}
else {
dstate_setinfo("input.L1-N.voltage", "%u", DevData.Uinp1);
dstate_setinfo("input.L2-N.voltage", "%u", DevData.Uinp2);
dstate_setinfo("input.L3-N.voltage", "%u", DevData.Uinp3);
dstate_setinfo("input.bypass.L1-N.voltage", "%u", DevData.Ubypass1);
dstate_setinfo("input.bypass.L2-N.voltage", "%u", DevData.Ubypass2);
dstate_setinfo("input.bypass.L3-N.voltage", "%u", DevData.Ubypass3);
}
if (output_monophase) {
dstate_setinfo("output.voltage", "%u", DevData.Uout1);
dstate_setinfo("output.power.percent", "%u", DevData.Pout1);
dstate_setinfo("ups.load", "%u", DevData.Pout1);
}
else {
dstate_setinfo("output.L1-N.voltage", "%u", DevData.Uout1);
dstate_setinfo("output.L2-N.voltage", "%u", DevData.Uout2);
dstate_setinfo("output.L3-N.voltage", "%u", DevData.Uout3);
dstate_setinfo("output.L1.power.percent", "%u", DevData.Pout1);
dstate_setinfo("output.L2.power.percent", "%u", DevData.Pout2);
dstate_setinfo("output.L3.power.percent", "%u", DevData.Pout3);
dstate_setinfo("ups.load", "%u", (DevData.Pout1+DevData.Pout2+DevData.Pout3)/3);
}
status_init();
/* AC Fail */
if (riello_test_bit(&DevData.StatusCode[0], 1))
status_set("OB");
else
status_set("OL");
/* LowBatt */
if ((riello_test_bit(&DevData.StatusCode[0], 1)) &&
(riello_test_bit(&DevData.StatusCode[0], 0)))
status_set("LB");
/* Standby */
if (!riello_test_bit(&DevData.StatusCode[0], 3))
status_set("OFF");
/* On Bypass */
if (riello_test_bit(&DevData.StatusCode[1], 3))
status_set("BYPASS");
/* Overload */
if (riello_test_bit(&DevData.StatusCode[4], 2))
status_set("OVER");
/* Buck */
if (riello_test_bit(&DevData.StatusCode[1], 0))
status_set("TRIM");
/* Boost */
if (riello_test_bit(&DevData.StatusCode[1], 1))
status_set("BOOST");
/* Replace battery */
if (riello_test_bit(&DevData.StatusCode[2], 0))
status_set("RB");
/* Charging battery */
if (riello_test_bit(&DevData.StatusCode[2], 2))
status_set("CHRG");
status_commit();
dstate_dataok();
if (getextendedOK) {
dstate_setinfo("output.L1.power", "%u", DevData.Pout1VA);
dstate_setinfo("output.L2.power", "%u", DevData.Pout2VA);
dstate_setinfo("output.L3.power", "%u", DevData.Pout3VA);
dstate_setinfo("output.L1.realpower", "%u", DevData.Pout1W);
dstate_setinfo("output.L2.realpower", "%u", DevData.Pout2W);
dstate_setinfo("output.L3.realpower", "%u", DevData.Pout3W);
dstate_setinfo("output.L1.current", "%u", DevData.Iout1);
dstate_setinfo("output.L2.current", "%u", DevData.Iout2);
dstate_setinfo("output.L3.current", "%u", DevData.Iout3);
}
poll_interval = 2;
countlost = 0;
/* if (get_ups_statuscode() != 0)
upsdebugx(2, "Communication is lost");
else {
}*/
/*
* ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, IGNCHARS);
*
* if (ret < STATUS_LEN) {
* upslogx(LOG_ERR, "Short read from UPS");
* dstate_datastale();
* return;
* }
*/
/* dstate_setinfo("var.name", ""); */
/* if (ioctl(upsfd, TIOCMGET, &flags)) {
* upslog_with_errno(LOG_ERR, "TIOCMGET");
* dstate_datastale();
* return;
* }
*/
/* status_init();
*
* if (ol)
* status_set("OL");
* else
* status_set("OB");
* ...
*
* status_commit();
*
* dstate_dataok();
*/
/*
* poll_interval = 2;
*/
}
void upsdrv_cleanup(void)
{
usb->close(udev);
USBFreeExactMatcher(reopen_matcher);
USBFreeRegexMatcher(regex_matcher);
free(usbdevice.Vendor);
free(usbdevice.Product);
free(usbdevice.Serial);
free(usbdevice.Bus);
}