/*
 * $Id: apc.c,v 2.0.1.5 1996/06/26 18:39:38 alexis Exp alexis $
 *
 * UPS Daemon
 * The Wild Wind Communications, 1995, 1996
 *
 * See file LICENSE for the distribution terms of this software.
 */

#include "upsd.h"
#include "apc.h"

char apc_SmartWriteBuffer[MAXAPCWRITEBUFFER];

/*
 * Polls APC SmartUPS value.
 *
 * Returns -1 on error, 1 if timed out or 0 if succeeded.
 */
int
apc_poll(val)
	struct ups_val *val;
{
	register int s;
	char *t;
	struct ups_reg *r;

	if((r = REGISTERID(val->id)) == NULL) {
		syslog(LOG_ERR, "apc_poll: incorrect register id");
		return -1;
	}
	if((t = alloca(POLLBUFLEN)) == NULL) {
		syslog(LOG_ERR, "apc_poll: alloca failed: %m");
		return -1;
	}

	for(;;) {
		flushport();

		if((s = writeport(r->cmd_data, r->cmd_size)) != r->cmd_size) {
			return -1;	/* Can't write to the port. */
		}

		if(r->size == 0) {
			return 0;	/* We don't expect anything back. */
		}

		if((s = readport(t, r->size, 1)) == -1) {
			return -1;	/* Can't read the port. */
		};

		if(s != r->size) {
			return 1;	/* Timed out on readport. */
		};

		switch(r->type) {
			case T_BINARY:
				bcopy(t, val->val.binary, (size_t) r->size - r->prec);
				((char *)val->val.binary)[r->size - r->prec] = '\0'; /* XXX: correct? */
				return 0;
				/* NOTREACHED */
			case T_HEX:
				val->val.number = (double)strtol(t, (char **)NULL, 16);
				return 0;
				/* NOTREACHED */
			case T_DEC:
				val->val.number = (double)strtol(t, (char **)NULL, 10);
				return 0;
				/* NOTREACHED */
			case T_OCT:
				val->val.number = (double)strtol(t, (char **)NULL, 8);
				return 0;
				/* NOTREACHED */
			case T_NUMBER:
				val->val.number = strtod(t, (char **)NULL);
				return 0;
				/* NOTREACHED */
			default:
				syslog(LOG_ERR, "apc_poll: incorrect value type");
				return -1;
				/* NOTREACHED */
		}
		/* NOTREACHED */
	}
	/* NOTREACHED */
	return -1;
}

/*
 * Tunes the value of APC SmartUPS.
 *
 * Returns -1 upon error, 1 if the string was not toggled, and zero
 * if succeeded.
 */
int
apc_tune(val)
	struct ups_val *val;
{
	register struct ups_reg *r, *tr;
	struct ups_val v0, v1, t;

	if((r = REGISTERID(val->id)) == NULL) {
		syslog(LOG_ERR, "apc_tune: incorrect value id to tune");
		return -1;
	}

	switch(r->mode) {
	case APC_TOGGLE:
		t.id = SMART_MODIFY;
		if((tr = REGISTERID(t.id)) == NULL) {
			syslog(LOG_ERR, "apc_tune: cannot find modify register");
			return -1;
		}
		if((t.val.binary = alloca(tr->size)) == NULL) {
			syslog(LOG_ERR, "apc_tune: cannot allocate memory in stack for modifier: %m");
			return -1;
		}
		v0.id = v1.id = r->id;
		if((r->type & T_TYPE) == T_BINARY) {
			if(((v0.val.binary = alloca(r->size)) == NULL) ||
			    ((v1.val.binary = alloca(r->size)) == NULL)) {
				syslog(LOG_ERR, "apc_tune: cannot allocate memory in stack for new values: %m");
				return -1;
			}
		}
		switch(apc_poll(&v0)) {
			case 1:
				return 1;
			case -1:
				return -1;
		}
		if((r->type & T_TYPE) == T_BINARY) {
			bcopy(v0.val.binary, v1.val.binary, r->size);
		} else {
			v1.val.number = v0.val.number;
		}
		for(;;) {
			if((r->type & T_TYPE) == T_BINARY) {
				if(!bcmp(val->val.binary, v1.val.binary, r->size)) {
					return 0;
				}
			} else {
				if(val->val.number == v1.val.number) {
					return 0;
				}
			}
			switch(apc_poll(&t)) {
				case 1:
					return 1;
				case -1:
					return -1;
			}

			if(bcmp(t.val.binary, apc_SmartUPS_OK, 2)) {
				syslog(LOG_ERR, "apc_tune: negative response: %*s",
				    r->size, t.val.binary);
				return 1;
			}
			switch(apc_poll(&v1)) {
				case 1:
					return 1;
				case -1:
					return -1;
			}
			if((r->type & T_TYPE) == T_BINARY) {
				if(!bcmp(v1.val.binary, v0.val.binary, r->size)) {
					syslog(LOG_WARNING, "apc_tune: toggle wraparound register %s", r->name);
					return 1;
				}
			} else {
				if(v1.val.number == v0.val.number) {
					syslog(LOG_WARNING, "apc_tune: toggle wraparound register %s", r->name);
					return 1;
				}
			}
		}
		/* NOTREACHED */
		break; /* APC_TOGGLE */
	case APC_WRITE:
		if((r->type & T_TYPE) != T_BINARY) {
			syslog(LOG_ERR, "apc_tune: only binary values can be overwritten");
			return -1;
		}
		t.id = SMART_WRITE;
		if((tr = REGISTERID(t.id)) == NULL) {
			syslog(LOG_ERR, "apc_tune: cannot find write register");
			return -1;
		}
		if((t.val.binary = alloca(tr->size)) == NULL) {
			syslog(LOG_ERR, "apc_tune: cannot allocate memory in stack for modifier: %m");
			return -1;
		}
		v0.id = r->id;
		if((v0.val.binary = alloca(r->size)) == NULL) {
			syslog(LOG_ERR, "apc_tune: cannot allocate memory in stack for new value: %m");
			return -1;
		}
		switch(apc_poll(&v0)) {
			case 1:
				return 1;
			case -1:
				return -1;
		}
		if(!bcmp(v0.val.binary, val->val.binary, r->size - r->prec)) {
			return 0;
		}
		apc_SmartWriteBuffer[0] = '-';
		bcopy(val->val.binary, &apc_SmartWriteBuffer[1], r->size);
		tr->cmd_size = r->size - r->prec + 1;
		switch(apc_poll(&t)) {
			case 1:
				return 1;
			case -1:
				return -1;
		}
		if(bcmp(t.val.binary, apc_SmartUPS_OK, 2)) {
			syslog(LOG_ERR, "apc_tune: negative response");
			return 1;	/* the response was not OK */
		}
		{
			struct timeval wd = APCWRITEDELAY;
			(void) select(0, NULL, NULL, NULL, &wd);
		}
		return 0;
		/* NOTREACHED */
		break; /* MTHD_WRITE */
	default:
		syslog(LOG_ERR, "apc_tune: this register cannot be modified");
		return -1;
		/* NOTREACHED */
	}
	/* NOTREACHED */
	return -1;
}


syntax highlighted by Code2HTML, v. 0.9.1