749 lines · 14.1 KB · C
RawDownload
/* * 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.