[Nut-upsdev] [RFC apcsmart V3 09/18] drivers/apcsmart: add APC_USERCTRL and APC_CRUCIAL

Michal Soltys soltys at ziu.info
Sat Mar 5 10:38:26 UTC 2011


Prepare the code for proper handling of override.* variables.

APC_USERCTRL - allows permanent overriding of the variable. Meaning
even if the ups supports reading and/or writing it, the driver will not
allow it (actually it will behave as if the variable wasn't supported at
all). This guarantees that the value set by the user in the
configuration file is respected.

Currently only battery.runtime.low overriding is allowed, which is
necessary for proper functioning of ignorelb (if user decided to
override that variable).

APC_CRUCIAL - this flag enforces checking if the variable is actually
supported.

Additionallly - the driver will ask user about reporting UPS models,
which don't support command querying, but do support firmware query - in
the other words - models which are not present in the compatibility table,
but should be included there.

oldapcsetup() also checks for presence of battery.charge now.

Signed-off-by: Michal Soltys <soltys at ziu.info>
---
 drivers/apcsmart.c |   56 +++++++++++++++++++++++++++++++++++++++++++--------
 drivers/apcsmart.h |   17 ++++++++++-----
 2 files changed, 58 insertions(+), 15 deletions(-)

diff --git a/drivers/apcsmart.c b/drivers/apcsmart.c
index ef66fce..bb8d394 100644
--- a/drivers/apcsmart.c
+++ b/drivers/apcsmart.c
@@ -388,7 +388,14 @@ static void do_capabilities(void)
 		entptr = &ptr[4];
 
 		vt = vartab_lookup_char(cmd);
-		valid = vt && ((loc == upsloc) || (loc == '4'));
+		/*
+		 * all the capabilities must first pass protocol_verify()
+		 * tests; some of the variables are allowed to be overriden by
+		 * a user, and we must not refresh or write them; after
+		 * earlier protocol verification, APC_PRESENT will not be set
+		 * on such variable
+		 */
+		valid = vt && ((loc == upsloc) || (loc == '4')) && (vt->flags & APC_PRESENT);
 
 		/* mark this as writable */
 		if (valid) {
@@ -464,6 +471,7 @@ static void oldapcsetup(void)
 
 	query_ups("ups.serial", 1);
 	query_ups("input.voltage", 1); /* This one may fail... no problem */
+	query_ups("battery.charge", 1); /* lots of upses support it, also necessary for ignorelb */
 
 	update_status();
 
@@ -474,7 +482,7 @@ static void oldapcsetup(void)
 
 static void protocol_verify(unsigned char cmd)
 {
-	int	i, found;
+	int	i, found, flags;
 
 	/* we might not care about this one */
 	if (strchr(CMD_IGN_CHARS, cmd))
@@ -489,7 +497,27 @@ static void protocol_verify(unsigned char cmd)
 			upsdebugx(3, "UPS supports variable [%s]",
 				apc_vartab[i].name);
 
-			/* load initial data */
+			if ((flags = dstate_getflags(apc_vartab[i].name)) >= 0) {
+				/*
+				 * variable has been defined at ups.conf level,
+				 * check if it's of an override.* kind and
+				 * overriding is allowed as per APC_USERCTRL
+				 * flag; if so - return without setting
+				 * APC_PRESENT
+				 */
+				if ((apc_vartab[i].flags & APC_USERCTRL) && (flags & ST_FLAG_IMMUTABLE))
+					return;
+			}
+
+			/* be extra careful about the presence of certain
+			 * variables */
+			if ((apc_vartab[i].flags & APC_CRUCIAL) && !query_ups(apc_vartab[i].name, 1)) {
+				upsdebugx(1, "Your UPS doesn't actually support variable [%s]", apc_vartab[i].name);
+				upsdebugx(1, "Please report this error");
+				return;
+			}
+
+			/* mark as present, load initial data */
 			apc_vartab[i].flags |= APC_PRESENT;
 			poll_data(&apc_vartab[i]);
 
@@ -553,7 +581,7 @@ static int firmware_table_lookup(void)
 	ret = ser_get_line(upsfd, buf, sizeof(buf), ENDCHAR, IGNCHARS, 
 		SER_WAIT_SEC, SER_WAIT_USEC);
 
-        /*
+	/*
 	 * Some UPSes support both 'V' and 'b'. As 'b' doesn't always return
 	 * firmware version, we attempt that only if 'V' doesn't work.
 	 */
@@ -599,25 +627,26 @@ static int firmware_table_lookup(void)
 			for (j = 0; j < strlen(compat_tab[i].cmdchars); j++)
 				protocol_verify(compat_tab[i].cmdchars[j]);
 
-			return 1;	/* matched */
+			return 2;	/* matched */
 		}
 	}
 
 	upsdebugx(2, "Not found in table - trying normal method");
-	return 0;			
+	return 1;
 }
 
 static void getbaseinfo(void)
 {
 	unsigned int	i;
-	int	ret = 0;
+	int	ret, retfw;
 	char 	*alrts, *cmds, temp[512];
 
 	/*
 	 *  try firmware lookup first; we could start with 'a', but older models
 	 *  sometimes return other things than a command set
 	 */
-	if (firmware_table_lookup() == 1)
+	retfw = firmware_table_lookup();
+	if (retfw == 2)
 		return;
 
 	upsdebugx(1, "APC - Attempting to find command set");
@@ -637,7 +666,16 @@ static void getbaseinfo(void)
 		SER_WAIT_SEC, SER_WAIT_USEC);
 
 	if ((ret < 1) || (!strcmp(temp, "NA"))) {
-		/* We have an old dumb UPS - go to specific code for old stuff */
+		/*
+		 * We have either an old dumb UPS, or there's no info in the
+		 * compatibility table. Go to specific code for the old stuff.
+		 */
+		if (retfw == 1) {
+			upsdebugx(1, "Your UPS supports firmware querying, but is unable to report its command set.");
+			upsdebugx(1, "It also wasn't found in the compatibility table - please consider");
+			upsdebugx(1, "reporting its firmware version and verifying which parts of smart");
+			upsdebugx(1, "protocol it supports.");
+		}
 		oldapcsetup();
 		return;
 	}
diff --git a/drivers/apcsmart.h b/drivers/apcsmart.h
index 939b6cb..c85f76b 100644
--- a/drivers/apcsmart.h
+++ b/drivers/apcsmart.h
@@ -78,8 +78,10 @@
 
 /* Driver command table flag values */
 
-#define APC_POLL	0x0001	/* Poll this variable regularly		*/
-#define APC_PRESENT	0x0004	/* Capability seen on this UPS		*/
+#define APC_POLL	0x0001	/* Poll this variable regularly, if present		*/
+#define APC_CRUCIAL	0x0002	/* "crucial" variable, always check if it's present	*/
+#define APC_PRESENT	0x0004	/* presence verified - command can be polled / executed	*/
+#define APC_USERCTRL	0x0008	/* variable's readout value can be controlled by a user	*/
 
 #define APC_RW		0x0010	/* read-write variable			*/
 #define APC_ENUM	0x0020	/* enumerated type			*/
@@ -113,8 +115,8 @@ apc_vartab_t	apc_vartab[] = {
 	{ "ups.firmware.old",  	0,			'V' },
 	{ "ups.firmware",  	0,			'b' },
 	{ "ups.firmware.aux",	0,			'v' },
-	{ "ups.model",		0,			0x01 },
 
+	{ "ups.model",		0,			0x01 },
 	{ "ups.serial",		0,			'n' },
 	{ "ups.mfr.date", 	0,			'm' },
 
@@ -169,7 +171,8 @@ apc_vartab_t	apc_vartab[] = {
 
 	{ "battery.date",	APC_STRING,		'x' },
 
-	{ "battery.charge",  	APC_POLL|APC_F_PERCENT,	'f' },
+	{ "battery.charge",  	APC_POLL|APC_F_PERCENT|
+				APC_CRUCIAL,		'f' },
 	{ "battery.charge.restart",  
 				APC_F_PERCENT,		'e' },
 
@@ -177,9 +180,11 @@ apc_vartab_t	apc_vartab[] = {
 	{ "battery.voltage.nominal", 
 				0,			'g' },
 
-	{ "battery.runtime", 	APC_POLL|APC_F_MINUTES,	'j' },
+	{ "battery.runtime", 	APC_POLL|APC_F_MINUTES|
+				APC_CRUCIAL,		'j' },
 	{ "battery.runtime.low",
-			 	APC_F_MINUTES,		'q' },
+				APC_F_MINUTES|APC_USERCTRL,
+							'q' },
 
 	{ "battery.packs", 	APC_F_DEC,		'>' },
 	{ "battery.packs.bad", 	APC_F_DEC,		'<' },
-- 
1.7.2.1




More information about the Nut-upsdev mailing list