/* * manage APC (American Power Corporation) UPS, notably shutting down * gracefully when power gets too low. enabled by "ups0=type=apc" in plan9.ini. * N.B.: connection to UPS is assumed to be on eia1 (2nd serial port) * at 2400 bps! due to hardcoded use of uartputc1, etc., probably can't * override port with "port=0" in plan9.ini entry. */#include "all.h"#include "mem.h"#include "io.h" //#define DEBUG if(cons.flags&apc.flag)print#define DEBUG print enum { OnBattery = 1, LowBattery = 2, Abnormal = 4, Uartspeed = 2400, Trustlowbatt = 0, // trust the low-battery bit? i don't.};enum { First, Reinit };enum { Timedout = -1, Kicked = -2, Event = -3 }; /* apcgetc return */ typedef enum { General, Cycle } CmdType;typedef enum { UpsOff, JustOff } Action; /* unimplemented to date */ typedef struct Capability { char cmd; int n; struct Capability *next; char *val[1];} Capability; static struct{ Lock; int flag; int gotreply; int kicked; int detected; Rendez r; Rendez doze; struct { Lock; Rendez; int count; uchar buf[100]; uchar *in; uchar *out; } rxq; struct { Rendez; int done; CmdType cmdtype; char cmd; void *arg1, *arg2; char resp[250]; /* response */ } user; /* hardware info */ char model[50]; char fwrev[10]; /* firmware revision */ Capability *cap; struct { ulong bits; ulong change; } status; /* battery info */ Timet battonticks; /* ticks with battery on */ Timet lastrepticks; /* ticks at last report */ ulong battpct; /* battery percent */ ulong battpctthen; ulong trigger; Action action; int port;} apc = {.trigger = 1000,.action = UpsOff,}; extern void uartspecial1(int port, void (*rx)(int), int (*tx)(void), int baud);extern void uartputc1(int c); voidstatuschange(int bit, int on){ if (((apc.status.bits&(bit)) != 0) != on) { apc.status.change |= bit; apc.status.bits = (apc.status.bits&~bit) | (on? bit: 0); }} static intkicked(void*){ return apc.kicked != 0;} static intapctxint(void){ return -1;} static voidapcputc(char c){ uartputc1(c);} static voidapcputs(char *s){ int c; while(c = *s++){ delay(10); apcputc(c); }} voidapcrxint(int c){ uchar *p; ilock(&apc.rxq); if (apc.rxq.count < sizeof(apc.rxq.buf)) { p = apc.rxq.in; *p++ = c; if(p >= apc.rxq.buf + sizeof(apc.rxq.buf)) p = apc.rxq.buf; apc.rxq.in = p; apc.rxq.count++; wakeup(&apc.rxq); } iunlock(&apc.rxq);} static intdone(void *p){ return *(int *)p != 0;} static inteitherdone(void *){ return apc.rxq.count != 0 || apc.kicked;} enum{ Freport = 1<<0, Floop = 1<<1, Fbreak = 1<<2,}; static struct{ uchar c; uchar type; uchar on; uchar action; char *msg;} atab[] = { '!', OnBattery, 1, Freport, 0, '$', OnBattery, 0, Freport, 0, '%', LowBattery, 1, Freport, 0, '+', LowBattery, 0, Freport, 0, '?', Abnormal, 1, Freport, 0, '=', Abnormal, 0, Freport, 0, '*', 0, 0, Floop, "apc: turning off\n", '#', 0, 0, Floop, "apc: replace battery\n", '&', 0, 0, Floop, "apc: check alarm register\n", 0x7c, 0, 0, Floop, "apc: eeprom modified\n",}; intapcgetc(int timo, int noevents){ int i, c; loop: if (timo < 0) sleep(&apc.rxq, eitherdone, 0); else tsleep(&apc.rxq, eitherdone, 0, timo); if (apc.kicked) return Kicked; ilock(&apc.rxq); if (apc.rxq.count == 0) { iunlock(&apc.rxq); if (timo >= 0) return Timedout; goto loop; } c = *apc.rxq.out++; if (apc.rxq.out >= apc.rxq.buf + sizeof(apc.rxq.buf)) apc.rxq.out = apc.rxq.buf; apc.rxq.count--; iunlock(&apc.rxq); for(i = 0; i < nelem(atab); i++){ if(c != atab[i].c) continue; if(atab[i].action == Floop){ print(atab[i].msg); goto loop; }; if(atab[i].action == Freport){ statuschange(atab[i].action, atab[i].on); print("apc: event %c\n", c); if (noevents) goto loop; return Event; } } return c;} char *apcgets(char *buf, int len, int timo){ char *q; int c; q = buf; while ((c = apcgetc(timo, 1)) >= 0 && c != '\r') if (q < buf + len - 1) *q++ = c; if (c < 0) return nil; c = apcgetc(timo, 1); if (c < 0 || c != '\n') return nil; *q = 0; return buf;} intapcexpect(char *s, int skiprubbish, int timo){ int first = 1, c; while (*s) { c = apcgetc(timo, 1); if (c < 0) return 0; if (*s == c) { s++; first = 0; continue; } if (!first) return 0; if (!skiprubbish) return 0; first = 0; } return 1;} intapcattention(void){ apcputc('Y'); if (!apcexpect("SM\r\n", 1, 1000)) return 0; apc.detected = 1; return 1;} char *apccmdstrresponse(char *cmd, char *buf, int len){ char *s; apcputs(cmd); if(s = apcgets(buf, len, 1000)) return s; print("APC asleep...\n"); if (!apcattention()) return nil; apcputs(cmd); return apcgets(buf, len, 1000);} char *apccmdresponse(char cmd, char *buf, int len){ char cmdstr[2]; cmdstr[0] = cmd; cmdstr[1] = 0; return apccmdstrresponse(cmdstr, buf, len);} static voidparsecap(char *capstr, char locale){ char cmd, lc, c; int n, el, i, j, p; while (*capstr) { char *s; Capability *cap; cmd = *capstr++; lc = *capstr++; n = *capstr++ - '0'; el = *capstr++ - '0'; p = lc == '4' || lc == locale; if (p) { cap = ialloc(sizeof *cap + sizeof s*(n - 1), 0); cap->cmd = cmd; cap->n = n; s = ialloc(n*(el + 1), 0); for (i = 0; i < n; i++) { cap->val[i] = s + i*(el + 1); cap->val[i][el] = 0; } } else cap = nil; for (i = 0; i < n; i++) for (j = 0; j < el; j++) { c = *capstr++; if (p) cap->val[i][j] = c; } if (p) { cap->next = apc.cap; apc.cap = cap; } }} static char *cyclecmd(Capability *cap, int i){ char *s, resp[10]; for (;;) { s = apccmdresponse(cap->cmd, resp, sizeof resp); if (s == nil || strcmp(resp, cap->val[i]) == 0) break; s = apccmdresponse('-', resp, sizeof resp); if (s == nil) break; } return s;} static ulonggetfloat(char *p, int dp){ ulong total; int afterdp = -1; total = 0; for (; *p; p++) if (*p == '.') afterdp = 0; else { total = total*10 + *p - '0'; if (afterdp >= 0) afterdp++; } if (afterdp < 0) afterdp = 0; while (afterdp > dp + 1) { afterdp--; total /= 10; } if (afterdp > dp) { afterdp--; total = (total + 5) / 10; } while (dp > afterdp) { afterdp++; total *= 10; } return total;} static intapcgetstatus(void){ char resp[10]; ulong status, change; do { change = apc.status.change; if (apccmdresponse('Q', resp, sizeof(resp)) == nil) return 0; } while (apc.status.change != change); status = strtoul(resp, 0, 16); if (status&(1 << 3) && apc.status.bits&OnBattery) { /* online? */ apc.status.bits &= ~OnBattery; apc.status.change |= OnBattery; } if (status&(1 << 4) && apc.status.bits&OnBattery) { /* on battery */// apc.status.bits |= OnBattery; apc.status.change |= OnBattery; } if (((status&(1 << 6)) != 0) != ((apc.status.bits&LowBattery) != 0)) { /* low battery */ apc.status.bits ^= LowBattery; apc.status.change |= LowBattery; } if (apccmdresponse('f', resp, sizeof(resp)) == nil) return 0; apc.battpct = getfloat(resp, 1); return 1;} /* * shutdown the file server gracefully. */static voidapcshuffle(char *why){ char resp[10]; print("Shutting down due to %s\n", why); wlock(&mainlock); /* don't process incoming requests from net */ sync("powerfail"); apccmdstrresponse("@000", resp, sizeof(resp)); print("APC responded: '%s'\n", resp); print("File server is now idling.\n"); delay(2000); splhi(); for (;;) idle();} static voidapckick(void){ if (apc.detected) { /* don't blather once per minute */ print("No APC ups detected\n"); apc.detected = 0; } apc.kicked = 0; tsleep(&apc.doze, kicked, 0, 1 * 60 * 1000);} static voidapcsetup(int reinit){ Capability *cap; int i; if (reinit) apckick(); for(;;){ while (!apcattention()) apckick(); apcputc(1); apcgets(apc.model, sizeof apc.model, -1); print("APC UPS model: %s\n", apc.model); apcputc('b'); apcgets(apc.fwrev, sizeof apc.fwrev, -1); print("Firmware revision: %s\n", apc.fwrev); apcputc(''); apcgets(apc.user.resp, sizeof apc.user.resp, -1); parsecap(apc.user.resp, apc.fwrev[strlen(apc.fwrev) - 1]); for (cap = apc.cap; cap; cap = cap->next) { print("%c %d", cap->cmd, cap->n); for (i = 0; i < cap->n; i++) print(" %s", cap->val[i]); print("\n"); } apc.status.change = 0; if (!apcgetstatus()) { apckick(); continue; } }} static voidapcbatton(void){ Timet now, nextreport, remaining; now = MACHP(0)->ticks; if (apc.status.change & OnBattery) { apc.lastrepticks = apc.battonticks = nextreport = now; apc.battpctthen = apc.battpct; } else nextreport = apc.lastrepticks + MS2TK(30 * 1000);Lines 1–500 of 749. Load more
/* * manage APC (American Power Corporation) UPS, notably shutting down * gracefully when power gets too low. enabled by "ups0=type=apc" in plan9.ini. * N.B.: connection to UPS is assumed to be on eia1 (2nd serial port) * at 2400 bps! due to hardcoded use of uartputc1, etc., probably can't * override port with "port=0" in plan9.ini entry. */ #include "all.h" #include "mem.h" #include "io.h" //#define DEBUG if(cons.flags&apc.flag)print #define DEBUG print enum { OnBattery = 1, LowBattery = 2, Abnormal = 4, Uartspeed = 2400, Trustlowbatt = 0, // trust the low-battery bit? i don't. }; enum { First, Reinit }; enum { Timedout = -1, Kicked = -2, Event = -3 }; /* apcgetc return */ typedef enum { General, Cycle } CmdType; typedef enum { UpsOff, JustOff } Action; /* unimplemented to date */ typedef struct Capability { char cmd; int n; struct Capability *next; char *val[1]; } Capability; static struct{ Lock; int flag; int gotreply; int kicked; int detected; Rendez r; Rendez doze; struct { Lock; Rendez; int count; uchar buf[100]; uchar *in; uchar *out; } rxq; struct { Rendez; int done; CmdType cmdtype; char cmd; void *arg1, *arg2; char resp[250]; /* response */ } user; /* hardware info */ char model[50]; char fwrev[10]; /* firmware revision */ Capability *cap; struct { ulong bits; ulong change; } status; /* battery info */ Timet battonticks; /* ticks with battery on */ Timet lastrepticks; /* ticks at last report */ ulong battpct; /* battery percent */ ulong battpctthen; ulong trigger; Action action; int port; } apc = { .trigger = 1000, .action = UpsOff, }; extern void uartspecial1(int port, void (*rx)(int), int (*tx)(void), int baud); extern void uartputc1(int c); void statuschange(int bit, int on) { if (((apc.status.bits&(bit)) != 0) != on) { apc.status.change |= bit; apc.status.bits = (apc.status.bits&~bit) | (on? bit: 0); } } static int kicked(void*) { return apc.kicked != 0; } static int apctxint(void) { return -1; } static void apcputc(char c) { uartputc1(c); } static void apcputs(char *s) { int c; while(c = *s++){ delay(10); apcputc(c); } } void apcrxint(int c) { uchar *p; ilock(&apc.rxq); if (apc.rxq.count < sizeof(apc.rxq.buf)) { p = apc.rxq.in; *p++ = c; if(p >= apc.rxq.buf + sizeof(apc.rxq.buf)) p = apc.rxq.buf; apc.rxq.in = p; apc.rxq.count++; wakeup(&apc.rxq); } iunlock(&apc.rxq); } static int done(void *p) { return *(int *)p != 0; } static int eitherdone(void *) { return apc.rxq.count != 0 || apc.kicked; } enum{ Freport = 1<<0, Floop = 1<<1, Fbreak = 1<<2, }; static struct{ uchar c; uchar type; uchar on; uchar action; char *msg; } atab[] = { '!', OnBattery, 1, Freport, 0, '$', OnBattery, 0, Freport, 0, '%', LowBattery, 1, Freport, 0, '+', LowBattery, 0, Freport, 0, '?', Abnormal, 1, Freport, 0, '=', Abnormal, 0, Freport, 0, '*', 0, 0, Floop, "apc: turning off\n", '#', 0, 0, Floop, "apc: replace battery\n", '&', 0, 0, Floop, "apc: check alarm register\n", 0x7c, 0, 0, Floop, "apc: eeprom modified\n", }; int apcgetc(int timo, int noevents) { int i, c; loop: if (timo < 0) sleep(&apc.rxq, eitherdone, 0); else tsleep(&apc.rxq, eitherdone, 0, timo); if (apc.kicked) return Kicked; ilock(&apc.rxq); if (apc.rxq.count == 0) { iunlock(&apc.rxq); if (timo >= 0) return Timedout; goto loop; } c = *apc.rxq.out++; if (apc.rxq.out >= apc.rxq.buf + sizeof(apc.rxq.buf)) apc.rxq.out = apc.rxq.buf; apc.rxq.count--; iunlock(&apc.rxq); for(i = 0; i < nelem(atab); i++){ if(c != atab[i].c) continue; if(atab[i].action == Floop){ print(atab[i].msg); goto loop; }; if(atab[i].action == Freport){ statuschange(atab[i].action, atab[i].on); print("apc: event %c\n", c); if (noevents) goto loop; return Event; } } return c; } char * apcgets(char *buf, int len, int timo) { char *q; int c; q = buf; while ((c = apcgetc(timo, 1)) >= 0 && c != '\r') if (q < buf + len - 1) *q++ = c; if (c < 0) return nil; c = apcgetc(timo, 1); if (c < 0 || c != '\n') return nil; *q = 0; return buf; } int apcexpect(char *s, int skiprubbish, int timo) { int first = 1, c; while (*s) { c = apcgetc(timo, 1); if (c < 0) return 0; if (*s == c) { s++; first = 0; continue; } if (!first) return 0; if (!skiprubbish) return 0; first = 0; } return 1; } int apcattention(void) { apcputc('Y'); if (!apcexpect("SM\r\n", 1, 1000)) return 0; apc.detected = 1; return 1; } char * apccmdstrresponse(char *cmd, char *buf, int len) { char *s; apcputs(cmd); if(s = apcgets(buf, len, 1000)) return s; print("APC asleep...\n"); if (!apcattention()) return nil; apcputs(cmd); return apcgets(buf, len, 1000); } char * apccmdresponse(char cmd, char *buf, int len) { char cmdstr[2]; cmdstr[0] = cmd; cmdstr[1] = 0; return apccmdstrresponse(cmdstr, buf, len); } static void parsecap(char *capstr, char locale) { char cmd, lc, c; int n, el, i, j, p; while (*capstr) { char *s; Capability *cap; cmd = *capstr++; lc = *capstr++; n = *capstr++ - '0'; el = *capstr++ - '0'; p = lc == '4' || lc == locale; if (p) { cap = ialloc(sizeof *cap + sizeof s*(n - 1), 0); cap->cmd = cmd; cap->n = n; s = ialloc(n*(el + 1), 0); for (i = 0; i < n; i++) { cap->val[i] = s + i*(el + 1); cap->val[i][el] = 0; } } else cap = nil; for (i = 0; i < n; i++) for (j = 0; j < el; j++) { c = *capstr++; if (p) cap->val[i][j] = c; } if (p) { cap->next = apc.cap; apc.cap = cap; } } } static char * cyclecmd(Capability *cap, int i) { char *s, resp[10]; for (;;) { s = apccmdresponse(cap->cmd, resp, sizeof resp); if (s == nil || strcmp(resp, cap->val[i]) == 0) break; s = apccmdresponse('-', resp, sizeof resp); if (s == nil) break; } return s; } static ulong getfloat(char *p, int dp) { ulong total; int afterdp = -1; total = 0; for (; *p; p++) if (*p == '.') afterdp = 0; else { total = total*10 + *p - '0'; if (afterdp >= 0) afterdp++; } if (afterdp < 0) afterdp = 0; while (afterdp > dp + 1) { afterdp--; total /= 10; } if (afterdp > dp) { afterdp--; total = (total + 5) / 10; } while (dp > afterdp) { afterdp++; total *= 10; } return total; } static int apcgetstatus(void) { char resp[10]; ulong status, change; do { change = apc.status.change; if (apccmdresponse('Q', resp, sizeof(resp)) == nil) return 0; } while (apc.status.change != change); status = strtoul(resp, 0, 16); if (status&(1 << 3) && apc.status.bits&OnBattery) { /* online? */ apc.status.bits &= ~OnBattery; apc.status.change |= OnBattery; } if (status&(1 << 4) && apc.status.bits&OnBattery) { /* on battery */ // apc.status.bits |= OnBattery; apc.status.change |= OnBattery; } if (((status&(1 << 6)) != 0) != ((apc.status.bits&LowBattery) != 0)) { /* low battery */ apc.status.bits ^= LowBattery; apc.status.change |= LowBattery; } if (apccmdresponse('f', resp, sizeof(resp)) == nil) return 0; apc.battpct = getfloat(resp, 1); return 1; } /* * shutdown the file server gracefully. */ static void apcshuffle(char *why) { char resp[10]; print("Shutting down due to %s\n", why); wlock(&mainlock); /* don't process incoming requests from net */ sync("powerfail"); apccmdstrresponse("@000", resp, sizeof(resp)); print("APC responded: '%s'\n", resp); print("File server is now idling.\n"); delay(2000); splhi(); for (;;) idle(); } static void apckick(void) { if (apc.detected) { /* don't blather once per minute */ print("No APC ups detected\n"); apc.detected = 0; } apc.kicked = 0; tsleep(&apc.doze, kicked, 0, 1 * 60 * 1000); } static void apcsetup(int reinit) { Capability *cap; int i; if (reinit) apckick(); for(;;){ while (!apcattention()) apckick(); apcputc(1); apcgets(apc.model, sizeof apc.model, -1); print("APC UPS model: %s\n", apc.model); apcputc('b'); apcgets(apc.fwrev, sizeof apc.fwrev, -1); print("Firmware revision: %s\n", apc.fwrev); apcputc(''); apcgets(apc.user.resp, sizeof apc.user.resp, -1); parsecap(apc.user.resp, apc.fwrev[strlen(apc.fwrev) - 1]); for (cap = apc.cap; cap; cap = cap->next) { print("%c %d", cap->cmd, cap->n); for (i = 0; i < cap->n; i++) print(" %s", cap->val[i]); print("\n"); } apc.status.change = 0; if (!apcgetstatus()) { apckick(); continue; } } } static void apcbatton(void) { Timet now, nextreport, remaining; now = MACHP(0)->ticks; if (apc.status.change & OnBattery) { apc.lastrepticks = apc.battonticks = nextreport = now; apc.battpctthen = apc.battpct; } else nextreport = apc.lastrepticks + MS2TK(30 * 1000);