ref: 144a386c96abf884f672a473676075e0c48e6502
author: Alex Musolino <musolinoa@gmail.com>
date: Sat Nov 28 23:13:06 EST 2020
initial commit
--- /dev/null
+++ b/dat.h
@@ -1,0 +1,1 @@
+
--- /dev/null
+++ b/finddevs.rc
@@ -1,0 +1,4 @@
+#!/bin/rc
+
+grep -li powerware '#u'/usb/*/ctl | while(f=`{read})
+ echo `{basename `{basename -d $f}} | sed 's/ep([0-9]+)\.([0-9]+)/\1/'
--- /dev/null
+++ b/fns.h
@@ -1,0 +1,3 @@
+void upssend(int, UpsRequest*);
+void upsrecv(int, UpsResponse*);
+uchar chksum(uchar*);
--- /dev/null
+++ b/main.c
@@ -1,0 +1,388 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <thread.h>
+#include <usb.h>
+
+typedef struct Ups Ups;
+
+struct Ups
+{
+ Dev *ctrl;
+ Dev *intr;
+ Biobuf *out;
+
+ char *id;
+ uint va;
+ uint ncpus;
+ uint nmaps;
+ uint nalarms;
+ uint cfgblksz;
+ uint statmapsz;
+ uint almlogsz;
+ uint evtlogsz;
+ uint topblksz;
+ uint cmdlstsz;
+ uint outblksz;
+ uint almblksz;
+};
+
+static void
+usage(void)
+{
+ fprint(2, "usage: %s dev\n", argv0);
+ threadexitsall("usage");
+}
+
+static uchar
+chksum(uchar *buf)
+{
+ int i;
+ uchar c;
+
+ c = 0;
+ for(i = 0; i < buf[1]+2; i++)
+ c -= buf[i];
+ return c;
+}
+
+
+static int
+setdesc(Dev *d, int val, int idx, uchar *buf, int len)
+{
+ return usbcmd(d, Rh2d|Rstd|Rdev, Rsetdesc, (val<<8)+idx, 0, buf, len);
+}
+
+static int
+sendrdcmd(Dev *d, uchar cmd)
+{
+ uchar buf[4];
+
+ buf[0] = 0xab;
+ buf[1] = 1;
+ buf[2] = cmd;
+ buf[3] = chksum(buf);
+ return setdesc(d, Dstr, 4, buf, 4);
+}
+
+static int
+validpkt(uchar *pkt)
+{
+ int i;
+ uchar c;
+
+ c = 0;
+ for(i = 0; i < 5+pkt[2]; i++)
+ c += pkt[i];
+ return c == 0;
+}
+
+static int
+readresp(Biobuf *bio, uchar cmd, uchar *buf, int buflen)
+{
+ uchar sum, seq;
+ int c, pktlen;
+ uchar *bp, *bend;
+
+ bp = buf;
+ bend = buf + buflen;
+Next:
+ for(;;){
+ if((c = Bgetc(bio)) < 0)
+ return -1;
+ if(c == 0xab)
+ break;
+ }
+ sum = 0xab;
+ if((c = Bgetc(bio)) < 0)
+ return -1;
+ if(c != cmd - 0x30)
+ goto Next;
+ sum += c;
+ if((c = Bgetc(bio)) < 0)
+ return -1;
+ pktlen = c;
+ sum += c;
+ if((c = Bgetc(bio)) < 0)
+ return -1;
+ seq = c;
+ sum += c;
+ while(pktlen-- > 0){
+ if(bp >= bend)
+ return -1;
+ if((c = Bgetc(bio)) < 0)
+ return -1;
+ *bp++ = c;
+ sum += c;
+ }
+ if((c = Bgetc(bio)) < 0)
+ return -1;
+ sum += c;
+ if(sum != 0)
+ return -1;
+ if((seq & 0x80) == 0)
+ goto Next;
+ return bp - buf;
+}
+
+int
+pwread(Ups *ups, uchar cmd, uchar *buf, int len)
+{
+ if(sendrdcmd(ups->ctrl, cmd) < 0)
+ return -1;
+ return readresp(ups->out, cmd, buf, len);
+}
+
+static int
+sendwrcmd(Dev *d, uchar *cmd, int cmdlen)
+{
+ uchar buf[128];
+
+ buf[0] = 0xab;
+ buf[1] = cmdlen;
+ memmove(&buf[2], cmd, cmdlen);
+ *(buf+2+cmdlen) = chksum(buf);
+ return setdesc(d, Dstr, 4, buf, 2+cmdlen+1);
+}
+
+static char*
+overallstatus(uchar v)
+{
+ switch(v){
+ case 0xF0:
+ return "ON BATTERY";
+ case 0xE0:
+ return "OUTPUT OVERLOAD";
+ case 0xD0:
+ return "RECTIFIER OVERLOAD";
+ case 0x90:
+ return "INVERTER RAMPING UP";
+ case 0x80:
+ return "SYNCING TO BYPASS";
+ case 0x70:
+ return "RECTIFIER RAMPING";
+ case 0x64:
+ return "ON MAINTENANCE BYPASS";
+ case 0x63:
+ return "ON BUCK/REDUCER";
+ case 0x62:
+ return "ON BOOST/STEP UP";
+ case 0x61:
+ return "ON DOUBLE BOOST";
+ case 0x60:
+ return "ON BYPASS";
+ case 0x51:
+ return "HIGH EFFICIENCY MODE";
+ case 0x50:
+ return "SYSTEM NORMAL";
+ case 0x40:
+ return "UPS SUPPORTING LOAD";
+ case 0x30:
+ return "UPS ON";
+ case 0x21:
+ return "OUTLET SWITCH OPEN";
+ case 0x20:
+ return "OUTLET BREAKER OPEN";
+ case 0x11:
+ return "MODULE FAILURE";
+ case 0x10:
+ return "UPS OFF";
+ default:
+ break;
+ }
+ return "UNKNOWN";
+}
+
+static char *topbits[8] = {
+ "bypass-installed",
+ "output-breaker-closed",
+ "on-bypass",
+ "on-battery",
+ "inverter-on",
+ "low-battery",
+ "rectifier-on",
+ "utility-present",
+};
+
+static char*
+topologystr(uchar v)
+{
+ static char buf[1024];
+ int i;
+ char *s, *e;
+
+ s = buf;
+ e = buf + sizeof(buf);
+ *s = 0;
+ for(i = 0; i < 8; i++){
+ if((v&(1<<i)) == 0)
+ continue;
+ s = seprint(s, e, ",%s", topbits[i]);
+ }
+ return buf+1;
+}
+
+static ushort
+getu16(uchar *b)
+{
+ ushort s;
+
+ s = b[1];
+ s <<= 8;
+ s |= b[0];
+ return s;
+}
+
+static void
+init(Ups *ups)
+{
+ int i, n;
+ uint v;
+ uchar buf[4096];
+
+ n = pwread(ups, 0x31, buf, sizeof(buf));
+ if(n < 0)
+ sysfatal("pwread: %r");
+
+ i = 0;
+ ups->ncpus = buf[i];
+ print("#cpus=%uhhd\n", buf[i]);
+
+ i += 2*buf[i]+1;
+
+ if(buf[i++] != 0){
+ v = buf[i];
+ }else{
+ v = getu16(buf+i);
+ i += 2;
+ }
+
+ ups->va = 50*v;
+ print("%udVA\n", ups->va);
+
+ //print("%uhhd output phase(s)°\n", buf[i]);
+ i += 2;
+
+ v = buf[i++];
+ ups->id = mallocz(v+1, 1);
+ strncpy(ups->id, (char*)buf+i, v);
+ i += v;
+
+ v = buf[i++];
+ ups->nmaps = v;
+ print("#maps=%uhhd\n", v);
+ i += v;
+
+ v = buf[i++];
+ ups->nalarms = v;
+ print("#alarms=%uhhd\n", v);
+ i += v;
+
+ v = getu16(buf+i);
+ ups->cfgblksz = v;
+ print("%uhd byte config block\n", v);
+ i += 2;
+
+ v = buf[i];
+ ups->statmapsz = v;
+ print("%uhhd byte statistics map\n", v);
+ i += buf[i] + 1;
+
+ v = getu16(buf+i);
+ ups->almlogsz = v;
+ print("%ud byte alarm log\n", v);
+ i += 2;
+
+ v = getu16(buf+i);
+ ups->evtlogsz = v;
+ print("%ud byte custom event log\n", v);
+ i += 2;
+
+ v = getu16(buf+i);
+ ups->topblksz = v;
+ print("%ud byte topology block\n", v);
+ i += 2;
+
+ i += 1;
+
+ v = getu16(buf+i);
+ ups->cmdlstsz = v;
+ print("%ud byte command list block\n", v);
+ i += 2;
+
+ v = getu16(buf+i);
+ ups->outblksz = v;
+ print("%ud byte outlet monitoring block\n", v);
+ i += 2;
+
+ v = getu16(buf+i);
+ ups->almblksz = v;
+ print("%ud byte alarm block\n", v);
+ i += 2;
+ assert(i == n);
+}
+
+static void
+stats(Ups *ups)
+{
+ int n;
+ uchar buf[128];
+
+ n = pwread(ups, 0x40, buf, sizeof(buf));
+ if(n < 0)
+ sysfatal("pwread: %r");
+
+ /*
+ fprint(2, "#cmds=%uhhd\n", buf[0]);
+ for(i = 0; i < n; i++){
+ fprint(2, "cmd[%d] = 0x%uhhx\n", i, buf[2+i]);
+ }
+ */
+
+ n = pwread(ups, 0x33, buf, sizeof(buf));
+ if(n < 0)
+ sysfatal("pwread: %r");
+ print("Overall Status: %s\n", overallstatus(buf[0]));
+ print("Topology Status: %s\n", topologystr(buf[1]));
+
+ n = pwread(ups, 0x34, buf, sizeof(buf));
+ if(n < 0)
+ sysfatal("pwread: %r");
+}
+
+void
+threadmain(int argc, char **argv)
+{
+ int i;
+ Ups ups;
+ Ep *ep;
+ Usbdev *ud;
+
+ ARGBEGIN{
+ default:
+ usage();
+ }ARGEND;
+
+ if(argc != 1)
+ usage();
+ ups.ctrl = getdev(*argv);
+ if(ups.ctrl == nil)
+ sysfatal("getdev: %r");
+ ud = ups.ctrl->usb;
+ ups.intr = nil;
+ for(i = 0; i < nelem(ud->ep); i++){
+ if((ep = ud->ep[i]) == nil)
+ continue;
+ if(ep->type == Eintr && ep->dir == Ein)
+ ups.intr = openep(ups.ctrl, ep->id);
+ }
+ if(ups.intr == nil)
+ sysfatal("could not find interrupt endpoint");
+ if(opendevdata(ups.intr, OREAD) < 0)
+ sysfatal("could not open interrupt endpoint: %r");
+ ups.out = Bfdopen(ups.intr->dfd, OREAD);
+ if(ups.out == nil)
+ sysfatal("open: %r");
+ //init(&ups);
+ stats(&ups);
+}
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,11 @@
+</$objtype/mkfile
+
+TARG=powerware
+OFILES=main.$O
+HFILES=dat.h
+
+LIB=/sys/src/cmd/nusb/lib/usb.a$O
+
+</sys/src/cmd/mkone
+
+CFLAGS=-I/sys/src/cmd/nusb/lib $CFLAGS