1) minor fixes

command '@' has been added to compat tables. All (?) APC models in smart
mode should handle it

ignore / alert sets have been adjusted to be more clear; lots of
comments added as well

upsdrv_shutdown_advanced() - fixed 'continue;' blocking certain
methods to be ever used

update author in comments

some other comments' updates

2) heavy stuff - ICANON

This patch add canonical processing mode. All reads and flushes are done
now by upsread() and upsflush(); '*' which is sort-of-alert (and behaves
as EOL), is handled in upsread(); upsread() behaviour is controlled
by SER_* flags; code has been updated to use new functions instead of
direct calls to ser_*();

Signed-off-by: Michal Soltys <[email protected]>
---
 drivers/apcsmart.c |  270 +++++++++++++++++++++++++++++++++++-----------------
 drivers/apcsmart.h |  127 ++++++++++++++++---------
 2 files changed, 267 insertions(+), 130 deletions(-)

diff --git a/drivers/apcsmart.c b/drivers/apcsmart.c
index d87cc5f..0a9e1bb 100644
--- a/drivers/apcsmart.c
+++ b/drivers/apcsmart.c
@@ -3,6 +3,7 @@
 
    Copyright (C) 1999  Russell Kroll <[email protected]>
              (C) 2000  Nigel Metheringham <[email protected]>
+             (C) 2011  Michal Soltys <[email protected]>
 
    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
@@ -197,20 +198,99 @@ static void alert_handler(char ch)
 	ups_status_set();
 }
 
-static int read_buf(char *buf, size_t buflen)
+static void upsflush(int flags)
 {
-	int	ret;
+	char temp[64];
 
-	ret = ser_get_line_alert(upsfd, buf, buflen, ENDCHAR, POLL_IGNORE,
-		POLL_ALERT, alert_handler, SER_WAIT_SEC, SER_WAIT_USEC);
+	if (flags & SER_AL) {
+		while(ser_get_line_alert(upsfd, temp, sizeof(temp), ENDCHAR, IGN_AACHARS, ALERT_CHARS, alert_handler, 0, 0) > 0);
+	} else {
+		while(ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, IGN_CHARS, 0, 0) > 0);
+	}
+}
 
-	if (ret < 1) {
-		ser_comm_fail("%s", ret ? strerror(errno) : "timeout");
-		return ret;
+/*
+ * we need a tiny bit different processing due to '*' and canonical mode; the
+ * function is subtly different from generic ser_get_line_alert()
+ */
+static int upsread(char *buf, size_t buflen, int flags)
+{
+	const char *iset = IGN_CHARS, *aset = "";
+	size_t	count = 0;
+	int	i, ret, sec = 3, usec = 0;
+	char	temp[512];
+
+	if (flags & SER_CC) {
+		iset = IGN_CCCHARS;
+		/*
+		 * 3 sec is near the edge of how much this command can take
+		 * until ENDCHAR shows up in the input; bump to 6 seconds
+		 * to be on the safe side
+		 */
+		sec = 6;
+	}
+	if (flags & SER_AL) {
+		iset = IGN_AACHARS;
+		aset = ALERT_CHARS;
+	}
+	if (flags & SER_SD) {
+		/* cut down timeout to 1.5 sec */
+		sec = 1;
+		usec = 500000;
+		flags |= SER_TO;
+	}
+	if (flags & SER_AX) {
+		flags |= SER_SD;
+	}
+
+	memset(buf, '\0', buflen);
+
+	while (count < buflen - 1) {
+		ret = select_read(upsfd, temp, sizeof(temp), sec, usec);
+
+		/* error or no timeout allowed */
+		if (ret < 0 || (ret == 0 && !(flags & SER_TO))) {
+			ser_comm_fail("%s", ret ? strerror(errno) : "timeout");
+			return ret;
+		}
+		/* ok, timeout is acceptable */
+		if (ret == 0 && (flags & SER_TO)) {
+			break;
+		}
+
+		for (i = 0; i < ret; i++) {
+			/* standard "line received" condition */
+			if ((count == buflen - 1) || (temp[i] == ENDCHAR)) {
+				ser_comm_good();
+				return count;
+			}
+			/*
+			 * '*' is set as a secondary EOL; return '*' only as a
+			 * reply to shutdown command in sdok(); otherwise next
+			 * select_read() will continue normally
+			 */
+			if ((flags & SER_AX) && temp[i] == '*') {
+				buf[0] = '*';
+				buf[1] = '\0';
+				ser_comm_good();
+				return 1;
+			}
+			/* ignore set */
+			if (strchr(iset, temp[i]) || temp[i] == '*') {
+				continue;
+			}
+			/* alert set */
+			if (strchr(aset, temp[i])) {
+				alert_handler(temp[i]);
+				continue;
+			}
+
+			buf[count++] = temp[i];
+		}
 	}
 
 	ser_comm_good();
-	return ret;
+	return count;
 }
 
 static int poll_data(apc_vartab_t *vt)
@@ -231,7 +311,7 @@ static int poll_data(apc_vartab_t *vt)
 		return 0;
 	}
 
-	if (read_buf(tmp, sizeof(tmp)) < 1) {
+	if (upsread(tmp, sizeof(tmp), SER_AL) < 1) {
 		dstate_datastale();
 		return 0;
 	}
@@ -270,8 +350,7 @@ static int query_ups(const char *var, int first)
 		return 0;
 
 	/* empty the input buffer (while allowing the alert handler to run) */
-	ret = ser_get_line_alert(upsfd, temp, sizeof(temp), ENDCHAR, 
-		POLL_IGNORE, POLL_ALERT, alert_handler, 0, 0);
+	upsflush(SER_AL);
 
 	ret = ser_send_char(upsfd, vt->cmd);
 
@@ -280,16 +359,8 @@ static int query_ups(const char *var, int first)
 		return 0;
 	}
 
-	ret = ser_get_line_alert(upsfd, temp, sizeof(temp), ENDCHAR, 
-		POLL_IGNORE, POLL_ALERT, alert_handler, SER_WAIT_SEC,
-		SER_WAIT_USEC);
-
-	if ((ret < 1) && (first == 0)) {
-		ser_comm_fail("%s", ret ? strerror(errno) : "timeout");
-		return 0;
-	}
-
-	ser_comm_good();
+	/* at the first run, read() is allowed to timeout */
+	ret = upsread(temp, sizeof(temp), SER_AL | (first ? SER_TO : 0));
 
 	if ((ret < 1) || (!strcmp(temp, "NA")))		/* not supported */
 		return 0;
@@ -326,9 +397,11 @@ static void do_capabilities(void)
 		return;
 	}
 
-	/* note different IGN set since ^Z returns things like # */
-	ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, 
-		MINIGNCHARS, SER_WAIT_SEC, SER_WAIT_USEC);
+	/*
+	 * note SER_CC - upsread() needs larger timeout grace and different
+	 * ignore set due to certain characters like '#' being received
+	 */
+	ret = upsread(temp, sizeof(temp), SER_CC);
 
 	if ((ret < 1) || (!strcmp(temp, "NA"))) {
 
@@ -422,7 +495,7 @@ static int update_status(void)
 
 	upsdebugx(4, "update_status");
 
-	ser_flush_in(upsfd, IGNCHARS, nut_debug_level);
+	upsflush(SER_AL);
 
 	ret = ser_send_char(upsfd, APC_STATUS);
 
@@ -432,7 +505,7 @@ static int update_status(void)
 		return 0;
 	}
 
-	ret = read_buf(buf, sizeof(buf));
+	ret = upsread(buf, sizeof(buf), SER_AL);
 
 	if ((ret < 1) || (!strcmp(buf, "NA"))) {
 		dstate_datastale();
@@ -547,8 +620,7 @@ static int firmware_table_lookup(void)
 		return 0;
 	}
 
-	ret = ser_get_line(upsfd, buf, sizeof(buf), ENDCHAR, IGNCHARS, 
-		SER_WAIT_SEC, SER_WAIT_USEC);
+	ret = upsread(buf, sizeof(buf), SER_TO);
 
         /*
 	 * Some UPSes support both 'V' and 'b'. As 'b' doesn't always return
@@ -563,11 +635,10 @@ static int firmware_table_lookup(void)
 			return 0;
 		}
 
-		ret = ser_get_line(upsfd, buf, sizeof(buf), ENDCHAR, IGNCHARS,
-			SER_WAIT_SEC, SER_WAIT_USEC);
+		ret = upsread(buf, sizeof(buf), SER_TO);
 
 		if (ret < 1) {
-			upslog_with_errno(LOG_ERR, "firmware_table_lookup: ser_get_line failed");
+			upslog_with_errno(LOG_ERR, "firmware_table_lookup: upsread failed");
 			return 0;
 		}
 	}
@@ -630,8 +701,7 @@ static void getbaseinfo(void)
 		return;
 	}
 
-	ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, IGNCHARS, 
-		SER_WAIT_SEC, SER_WAIT_USEC);
+	ret = upsread(temp, sizeof(temp), SER_TO);
 
 	if ((ret < 1) || (!strcmp(temp, "NA"))) {
 		/* We have an old dumb UPS - go to specific code for old stuff */
@@ -640,12 +710,13 @@ static void getbaseinfo(void)
 	}
 
 	upsdebugx(1, "APC - Parsing out command set");
-	/* We have the version.alert.cmdchars string
-	   NB the alert chars are normally in IGNCHARS
-	   so will have been pretty much edited out.
-	   You will need to change the ser_get_line above if
-	   you want to check those out too....
-	*/
+	/*
+	 * note that upsread() above will filter commands overlapping with
+	 * alerts; we don't really care about those for the needed
+	 * functionality - but keep in mind 'a' reports them, should you ever
+	 * need them; in such case, it would be best to e.g. add SER_CS,
+	 * IGN_CSCHARS and adjust upsread() accordingly
+	 */
  	alrts = strchr(temp, '.');
 	if (alrts == NULL) {
 		fatalx(EXIT_FAILURE, "Unable to split APC version string");
@@ -681,7 +752,7 @@ static int do_cal(int start)
 		return STAT_INSTCMD_HANDLED;		/* FUTURE: failure */
 	}
 
-	ret = read_buf(temp, sizeof(temp));
+	ret = upsread(temp, sizeof(temp), SER_AL);
 
 	/* if we can't check the current calibration status, bail out */
 	if ((ret < 1) || (!strcmp(temp, "NA")))
@@ -707,7 +778,7 @@ static int do_cal(int start)
 			return STAT_INSTCMD_HANDLED;	/* FUTURE: failure */
 		}
 
-		ret = read_buf(temp, sizeof(temp));
+		ret = upsread(temp, sizeof(temp), SER_AL);
 
 		if ((ret < 1) || (!strcmp(temp, "NA")) || (!strcmp(temp, "NO"))) {
 			upslogx(LOG_WARNING, "Stop calibration failed: %s", 
@@ -734,7 +805,7 @@ static int do_cal(int start)
 		return STAT_INSTCMD_HANDLED;	/* FUTURE: failure */
 	}
 
-	ret = read_buf(temp, sizeof(temp));
+	ret = upsread(temp, sizeof(temp), SER_AL);
 
 	if ((ret < 1) || (!strcmp(temp, "NA")) || (!strcmp(temp, "NO"))) {
 		upslogx(LOG_WARNING, "Start calibration failed: %s", temp);
@@ -759,12 +830,10 @@ static int smartmode(void)
 			return 0;
 		}
 
-		ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, 
-			IGNCHARS, SER_WAIT_SEC, SER_WAIT_USEC);
+		ret = upsread(temp, sizeof(temp), SER_TO);
 
-		if (ret > 0)
-			if (!strcmp(temp, "SM"))
-				return 1;	/* success */
+		if (ret > 0 && !strcmp(temp, "SM"))
+			return 1;	/* success */
 
 		sleep(1);	/* wait before trying again */
 
@@ -778,8 +847,7 @@ static int smartmode(void)
 		}
 
 		/* eat the response (might be NA, might be something else) */
-		ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, 
-			IGNCHARS, SER_WAIT_SEC, SER_WAIT_USEC);
+		upsread(temp, sizeof(temp), SER_TO);
 	}
 
 	return 0;	/* failure */
@@ -792,8 +860,12 @@ static int sdok(void)
 {
 	char temp[16];
 
-	ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, IGNCHARS, SER_WAIT_SEC, SER_WAIT_USEC);
-	upsdebugx(4, "sdok: got \"%s\"", temp);
+	/*
+	 * older upses on failed commands might just timeout, we cut down
+	 * timeout grace in upsread though
+	 */
+	upsread(temp, sizeof(temp), SER_AX);
+	upsdebugx(4, "shutdown reply: got \"%s\"", temp);
 
 	if (!strcmp(temp, "*") || !strcmp(temp, "OK")) {
 		upsdebugx(4, "Last issued shutdown command succeeded");
@@ -807,7 +879,7 @@ static int sdok(void)
 /* soft hibernate: S - working only when OB, otherwise ignored */
 static int sdcmd_S(int dummy)
 {
-	ser_flush_in(upsfd, IGNCHARS, nut_debug_level);
+	upsflush(0);
 
 	upsdebugx(1, "Issuing soft hibernate");
 	ser_send_char(upsfd, APC_CMD_SOFTDOWN);
@@ -816,15 +888,18 @@ static int sdcmd_S(int dummy)
 }
 
 /* soft hibernate, hack version for CS 350 */
-static int sdcmd_CS(int tval)
+static int sdcmd_CS(int status)
 {
+	char temp[16];
+
 	upsdebugx(1, "Using CS 350 'force OB' shutdown method");
-	if (tval & APC_STAT_OL) {
+	if (status & APC_STAT_OL) {
 		upsdebugx(1, "On-line - forcing OB temporarily");
 		ser_send_char(upsfd, 'U');
-		usleep(UPSDELAY);
+		/* eat response */
+		upsread(temp, sizeof(temp), SER_SD);
 	}
-	return sdcmd_S(tval);
+	return sdcmd_S(0);
 }
 
 /*
@@ -836,7 +911,7 @@ static int sdcmd_ATn(int cnt)
 {
 	int n = 0, mmax, ret;
 	const char *strval;
-	char timer[4];
+	char temp[16];
 
 	mmax = cnt == 2 ? 99 : 999;
 
@@ -847,14 +922,14 @@ static int sdcmd_ATn(int cnt)
 			n = 0;
 	}
 
-	snprintf(timer, sizeof(timer), "%.*d", cnt, n);
+	snprintf(temp, sizeof(temp), "%.*d", cnt, n);
 
-	ser_flush_in(upsfd, IGNCHARS, nut_debug_level);
+	upsflush(0);
 	upsdebugx(1, "Issuing hard hibernate with %d minutes additional wakeup delay", n*6);
 
 	ser_send_char(upsfd, APC_CMD_GRACEDOWN);
 	usleep(CMDLONGDELAY);
-	ser_send_pace(upsfd, UPSDELAY, "%s", timer);
+	ser_send_pace(upsfd, UPSDELAY, "%s", temp);
 
 	ret = sdok();
 	if (ret || cnt == 3)
@@ -867,8 +942,8 @@ static int sdcmd_ATn(int cnt)
 	 * silent (YMMV);
 	 */
 	ser_send_char(upsfd, APC_CMD_GRACEDOWN);
-	usleep(UPSDELAY);
-	ser_flush_in(upsfd, IGNCHARS, nut_debug_level);
+	/* eat response */
+	upsread(temp, sizeof(temp), SER_SD);
 
 	return 0;
 }
@@ -876,7 +951,7 @@ static int sdcmd_ATn(int cnt)
 /* shutdown: K - delayed poweroff */
 static int sdcmd_K(int dummy)
 {
-	ser_flush_in(upsfd, IGNCHARS, nut_debug_level);
+	upsflush(0);
 	upsdebugx(1, "Issuing delayed poweroff");
 
 	ser_send_char(upsfd, APC_CMD_SHUTDOWN);
@@ -889,7 +964,7 @@ static int sdcmd_K(int dummy)
 /* shutdown: Z - immediate poweroff */
 static int sdcmd_Z(int dummy)
 {
-	ser_flush_in(upsfd, IGNCHARS, nut_debug_level);
+	upsflush(0);
 	upsdebugx(1, "Issuing immediate poweroff");
 
 	ser_send_char(upsfd, APC_CMD_OFF);
@@ -1005,16 +1080,18 @@ static void upsdrv_shutdown_advanced(int status)
 	 */
 
 	for (i = 0; i < strlen(strval); i++) {
-		if (strval[i] - 48 == SDIDX_CS) {
-			n = status;
-		} else if (strval[i] - 48 == SDIDX_AT3N) {
-			n = 3;
-		} else if (strval[i] - 48 == SDIDX_AT2N) {
-			n = 2;
-		} else {
-			/* the value of 'n' needs to be set at this point, but it isn't */
-			continue;
+		switch (strval[i] - 48) {
+			case SDIDX_CS:
+				n = status;
+				break;
+			case SDIDX_AT3N:
+				n = 3;
+				break;
+			case SDIDX_AT2N:
+			default:
+				n = 2;
 		}
+
 		if (sdlist[strval[i] - 48](n))
 			break;	/* finish if command succeeded */
 	}
@@ -1034,8 +1111,7 @@ void upsdrv_shutdown(void)
 	ret = ser_send_char(upsfd, APC_STATUS);
 
 	if (ret == 1) {
-		ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR,
-			IGNCHARS, SER_WAIT_SEC, SER_WAIT_USEC);
+		ret = upsread(temp, sizeof(temp), SER_SD);
 
 		if (ret < 1) {
 			upsdebugx(1, "Status read failed ! Assuming on battery state");
@@ -1105,7 +1181,7 @@ static int setvar_enum(apc_vartab_t *vt, const char *val)
 	char	orig[256], temp[256];
 	const char	*ptr;
 
-	ser_flush_in(upsfd, IGNCHARS, nut_debug_level);
+	upsflush(SER_AL);
 	ret = ser_send_char(upsfd, vt->cmd);
 
 	if (ret != 1) {
@@ -1113,7 +1189,7 @@ static int setvar_enum(apc_vartab_t *vt, const char *val)
 		return STAT_SET_HANDLED;	/* FUTURE: failed */
 	}
 
-	ret = read_buf(orig, sizeof(orig));
+	ret = upsread(orig, sizeof(orig), SER_AL);
 
 	if ((ret < 1) || (!strcmp(orig, "NA")))
 		return STAT_SET_HANDLED;	/* FUTURE: failed */
@@ -1137,7 +1213,7 @@ static int setvar_enum(apc_vartab_t *vt, const char *val)
 		}
 
 		/* this should return either OK (if rotated) or NO (if not) */
-		ret = read_buf(temp, sizeof(temp));
+		ret = upsread(temp, sizeof(temp), SER_AL);
 
 		if ((ret < 1) || (!strcmp(temp, "NA")))
 			return STAT_SET_HANDLED;	/* FUTURE: failed */
@@ -1156,7 +1232,7 @@ static int setvar_enum(apc_vartab_t *vt, const char *val)
 			return STAT_SET_HANDLED;	/* FUTURE: failed */
 		}
 
-		ret = read_buf(temp, sizeof(temp));
+		ret = upsread(temp, sizeof(temp), SER_AL);
 
 		if ((ret < 1) || (!strcmp(temp, "NA")))
 			return STAT_SET_HANDLED;	/* FUTURE: failed */
@@ -1199,7 +1275,7 @@ static int setvar_string(apc_vartab_t *vt, const char *val)
 	int	ret;
 	char	temp[256];
 
-	ser_flush_in(upsfd, IGNCHARS, nut_debug_level);
+	upsflush(SER_AL);
 	ret = ser_send_char(upsfd, vt->cmd);
 
 	if (ret != 1) {
@@ -1207,7 +1283,7 @@ static int setvar_string(apc_vartab_t *vt, const char *val)
 		return STAT_SET_HANDLED;	/* FUTURE: failed */
 	}
 
-	ret = read_buf(temp, sizeof(temp));
+	ret = upsread(temp, sizeof(temp), SER_AL);
 
 	if ((ret < 1) || (!strcmp(temp, "NA")))
 		return STAT_SET_HANDLED;	/* FUTURE: failed */
@@ -1252,7 +1328,7 @@ static int setvar_string(apc_vartab_t *vt, const char *val)
 		usleep(UPSDELAY);
 	}
 
-	ret = read_buf(temp, sizeof(temp));
+	ret = upsread(temp, sizeof(temp), SER_AL);
 
 	if (ret < 1) {
 		upslogx(LOG_ERR, "setvar_string: short final read");
@@ -1302,7 +1378,7 @@ static int do_cmd(apc_cmdtab_t *ct)
 	int	ret;
 	char	buf[SMALLBUF];
 
-	ser_flush_in(upsfd, IGNCHARS, nut_debug_level);
+	upsflush(SER_AL);
 	ret = ser_send_char(upsfd, ct->cmd);
 
 	if (ret != 1) {
@@ -1322,7 +1398,7 @@ static int do_cmd(apc_cmdtab_t *ct)
 		}
 	}
 
-	ret = read_buf(buf, sizeof(buf));
+	ret = upsread(buf, sizeof(buf), SER_AL);
 
 	if (ret < 1)
 		return STAT_INSTCMD_HANDLED;		/* FUTURE: failed */
@@ -1414,7 +1490,8 @@ void upsdrv_makevartable(void)
 
 void upsdrv_initups(void)
 {
-	char	*cable;
+	struct termios tio;
+	char *cable;
 
 	upsfd = ser_open(device_path);
 	ser_set_speed(upsfd, device_path, B2400);
@@ -1424,6 +1501,27 @@ void upsdrv_initups(void)
 	if (cable && !strcasecmp(cable, ALT_CABLE_1))
 		init_serial_0095B();
 
+	/* enable canonical processing */
+	memset(&tio, 0, sizeof(tio));
+	if (tcgetattr(upsfd, &tio) != 0)
+		fatal_with_errno(EXIT_FAILURE, "tcgetattr(%s)", device_path);
+
+	tio.c_lflag |= ICANON;
+	tio.c_lflag &= ~ISIG;
+
+	tio.c_iflag |= IGNCR;
+	tio.c_iflag &= ~(IXON | IXOFF);
+
+	tio.c_cc[VERASE] = _POSIX_VDISABLE;
+	tio.c_cc[VKILL]  = _POSIX_VDISABLE;
+
+	tio.c_cc[VEOL] = '*';	/* specially handled in upsread() */
+	tio.c_cc[VEOL2] = _POSIX_VDISABLE;
+	tio.c_cc[VEOF] = _POSIX_VDISABLE;
+
+	if (tcsetattr(upsfd, TCSANOW, &tio) != 0)
+		fatal_with_errno(EXIT_FAILURE, "tcsetattr(%s)", device_path);
+
 	/* make sure we wake up if the UPS sends alert chars to us */
 	extrafd = upsfd;
 }
@@ -1465,7 +1563,7 @@ void upsdrv_updateinfo(void)
 
 	/* try to wake up a dead ups once in awhile */
 	if ((dstate_is_stale()) && (!smartmode())) {
-		ser_comm_fail("Communications with UPS lost - check cabling");
+		upslogx(LOG_ERR, "Communications with UPS lost - check cabling");
 
 		/* reset this so a full update runs when the UPS returns */
 		last_full = 0;
diff --git a/drivers/apcsmart.h b/drivers/apcsmart.h
index f143cfa..8ebb249 100644
--- a/drivers/apcsmart.h
+++ b/drivers/apcsmart.h
@@ -2,6 +2,7 @@
 
    Copyright (C) 1999  Russell Kroll <[email protected]>
              (C) 2000  Nigel Metheringham <[email protected]>
+             (C) 2011  Michal Soltys <[email protected]>
 
    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
@@ -25,27 +26,58 @@
 
 #define APC_TABLE_VERSION	"version 2.2"
 
+/*
+ * alerts and other stuff for quick reference:
+ *
+ * $ OL
+ * ! OB
+ * % LB
+ * + not LB
+ * # RB
+ * ? OVER
+ * = not OVER
+ * * powering down now (only older models ?), handled by upsread()
+ * 	otherwise ignored (it doesn't have to be in ignore sets)
+ *
+ * | eeprom change
+ * & check alarm register for fail
+ * ~ ???
+ */
+
+/*
+ * old ones for reference:
+ * 	#define IGNCHARS "\015+$|!~%?=#&"
+ * 	#define POLL_IGNORE "\015&|"
+ *	#define POLL_ALERT "$!%+#?="
+ *	#define MINIGNCHARS "\015+$|!"
+ * notice ~ that was present in IGNCHARS, but not in POLL_IGNORE - this kinda
+ * didn't make sense (?); new versions doesn't filter ~, but keep that in mind
+ * in case something obscure surfaces
+ * due to switch to ICANON tty mode, we removed \015 from ignored characters,
+ * as it's handled by IGNCR at read() level
+ */
+
 /* Basic UPS reply line structure */
 #define ENDCHAR 10		/* APC ends responses with LF */
 
-/* characters ignored by default */
-#define IGNCHARS "\015+$|!~%?=#&"	/* special characters to ignore */
+/* what to ignore during alert aware serial reads */
+#define IGN_AACHARS "|&"
 
-/* these one is used only during startup, due to ^Z sending certain characters such as # */
-#define MINIGNCHARS "\015+$|!"	/* minimum set of special characters to ignore */
+/* what alert_handler() should care about */
+#define ALERT_CHARS "$!%+#?="
 
-/* normal polls: characters we don't want to parse (including a few alerts) */
-#define POLL_IGNORE "\015&|"
+/* characters ignored by alertless reads */
+#define IGN_CHARS IGN_AACHARS ALERT_CHARS
 
-/* alert characters we care about - OL, OB, LB, not LB, RB, OVER, not OVER */
-#define POLL_ALERT "$!%+#?="
+/*
+ * these ones are used only during capability read, due to ^Z sending certain
+ * characters such as #; it seems it could be equal to just IGN_CHARS w/o #
+ */
+#define IGN_CCCHARS "|$!+"	/* capability check ignore set */
 
 #define UPSDELAY	  50000	/* slow down multicharacter commands        */
 #define CMDLONGDELAY	1500000	/* some commands need a 1.5s gap for safety */
 
-#define SER_WAIT_SEC	3	/* wait up to 3.0 sec for ser_get calls */
-#define SER_WAIT_USEC	0
-
 /* dangerous instant commands must be reconfirmed within a 12 second window */
 #define MINCMDTIME	3
 #define MAXCMDTIME	15
@@ -53,6 +85,13 @@
 /* it only does two strings, and they're both the same length */
 #define APC_STRLEN	8
 
+/* how upsread() should behave */
+#define SER_AL  0x01		/* run with alarm handler */
+#define SER_TO  0x02		/* allow timeout without error */
+#define SER_CC  0x04		/* prepare for capability check (^Z) processing */
+#define SER_SD  0x08		/* prepare for shutdown command processing */
+#define SER_AX  0x10		/* prepare for '*' handling */
+
 /* --------------- */
 
 /* status bits */
@@ -79,13 +118,13 @@
 /* Driver command table flag values */
 
 #define APC_POLL	0x0001	/* Poll this variable regularly		*/
-#define APC_PRESENT	0x0004	/* Capability seen on this UPS		*/
+#define APC_PRESENT	0x0002	/* Capability seen on this UPS		*/
 
 #define APC_RW		0x0010	/* read-write variable			*/
 #define APC_ENUM	0x0020	/* enumerated type			*/
 #define APC_STRING	0x0040	/* string				*/
 
-#define APC_NASTY	0x0100	/* Nasty command - take care		*/
+#define APC_NASTY	0x0100	/* Nasty command - must be reconfirmed	*/
 #define APC_REPEAT	0x0200	/* Command needs sending twice		*/
 
 #define APC_FORMATMASK	0xFF0000 /* Mask for apc data formats */
@@ -100,12 +139,12 @@
 #define APC_F_MINUTES	0x110000 /* Time in minutes */
 #define APC_F_HOURS	0x120000 /* Time in hours */
 #define APC_F_REASON	0x130000 /* Reason of transfer */
-#define APC_F_LEAVE	0	/* Just pass this through */
+#define APC_F_LEAVE	0x000000 /* Just pass this through */
 
 typedef struct {
-	const	char	*name;		/* the variable name */
-	unsigned int	flags;	 	/* various flags		*/
-	char		cmd;		/* command character */
+	const	char	*name;		/* the variable name	*/
+	unsigned int	flags;	 	/* various flags	*/
+	char		cmd;		/* command character	*/
 } apc_vartab_t;
 
 apc_vartab_t	apc_vartab[] = {
@@ -252,40 +291,40 @@ struct {
 	int	flags;
 } compat_tab[] = {
 	/* APC Matrix */
-	{ "0XI",	"789ABCDEFGKLMNOPQRSTUVWXYZcefgjklmnopqrsuwxz/<>\\^\014\026", 0 },
-	{ "0XM",	"789ABCDEFGKLMNOPQRSTUVWXYZcefgjklmnopqrsuwxz/<>\\^\014\026", 0 },
-	{ "0ZI",	"79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz/<>", 0 },
-	{ "5UI",	"79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz/<>", 0 },
-	{ "5ZM",	"79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz/<>", 0 },
+	{ "0XI",	"@789ABCDEFGKLMNOPQRSTUVWXYZcefgjklmnopqrsuwxz/<>\\^\014\026", 0 },
+	{ "0XM",	"@789ABCDEFGKLMNOPQRSTUVWXYZcefgjklmnopqrsuwxz/<>\\^\014\026", 0 },
+	{ "0ZI",	"@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz/<>", 0 },
+	{ "5UI",	"@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz/<>", 0 },
+	{ "5ZM",	"@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz/<>", 0 },
 	/* APC600 */
-	{ "6QD",	"79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 },
-	{ "6QI",	"79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 },
-	{ "6TD",	"79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 },
-	{ "6TI",	"79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 },
+	{ "6QD",	"@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 },
+	{ "6QI",	"@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 },
+	{ "6TD",	"@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 },
+	{ "6TI",	"@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 },
 	/* SmartUPS 900 */
-	{ "7QD",	"79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 },
-	{ "7QI",	"79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 },
-	{ "7TD",	"79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 },
-	{ "7TI",	"79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 },
+	{ "7QD",	"@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 },
+	{ "7QI",	"@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 },
+	{ "7TD",	"@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 },
+	{ "7TI",	"@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 },
 	/* SmartUPS 900I */
-	{ "7II",	"79ABCEFGKLMNOPQSUVWXYZcfg", 0 },
+	{ "7II",	"@79ABCEFGKLMNOPQSUVWXYZcfg", 0 },
 	/* SmartUPS 2000I */
-	{ "9II",	"79ABCEFGKLMNOPQSUVWXYZcfg", 0 },
-	{ "9GI",	"79ABCEFGKLMNOPQSUVWXYZcfg", 0 },
+	{ "9II",	"@79ABCEFGKLMNOPQSUVWXYZcfg", 0 },
+	{ "9GI",	"@79ABCEFGKLMNOPQSUVWXYZcfg", 0 },
 	/* SmartUPS 1250 */
-	{ "8QD",	"79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 },
-	{ "8QI",	"79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 },
-	{ "8TD",	"79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 },
-	{ "8TI",	"79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 },
+	{ "8QD",	"@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 },
+	{ "8QI",	"@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 },
+	{ "8TD",	"@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 },
+	{ "8TI",	"@79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 },
 	/* CS 350 */
-	{ "5.4.D",	"\1ABPQRSUYbdfgjmnx9",	0 },
+	{ "5.4.D",	"@\1ABPQRSUYbdfgjmnx9",	0 },
 	/* Smart-UPS 600 */
-	{  "D9",	"789ABCEFGKLMNOPQRSUVWXYZ", 0 },
-	{  "D8",	"789ABCEFGKLMNOPQRSUVWXYZ", 0 },
-	{  "D7",	"789ABCEFGKLMNOPQRSUVWXYZ", 0 },
-	{  "D6",	"789ABCEFGKLMNOPQRSUVWXYZ", 0 },
-	{  "D5",	"789ABCEFGKLMNOPQRSUVWXYZ", 0 },
-	{  "D4",	"789ABCEFGKLMNOPQRSUVWXYZ", 0 },
+	{  "D9",	"@789ABCEFGKLMNOPQRSUVWXYZ", 0 },
+	{  "D8",	"@789ABCEFGKLMNOPQRSUVWXYZ", 0 },
+	{  "D7",	"@789ABCEFGKLMNOPQRSUVWXYZ", 0 },
+	{  "D6",	"@789ABCEFGKLMNOPQRSUVWXYZ", 0 },
+	{  "D5",	"@789ABCEFGKLMNOPQRSUVWXYZ", 0 },
+	{  "D4",	"@789ABCEFGKLMNOPQRSUVWXYZ", 0 },
 
 	{ NULL,		NULL,			0 },
 };
_______________________________________________
Nut-upsdev mailing list
[email protected]
http://lists.alioth.debian.org/mailman/listinfo/nut-upsdev

Reply via email to