Michal Soltys
2011-Mar-05 10:38 UTC
[Nut-upsdev] [RFC apcsmart V3 00/18] apcsmart driver updates
Sorry for a bit longer delay than I anticipated, I was stuffed with the work. This is the next iteration of the patch adding some functionality to apcsmart driver, and relying on 'ignorelb' recently added. Follow up from previous thread: http://www.mail-archive.com/nut-upsdev at lists.alioth.debian.org/msg02331.html Main differences is that V3 is split into many small patches, so the reviewing changes should be much easier. There're also a few additional changes in later commits. I'll do more tests in the following week, though everything seems to be working fine so far. Still to do: manpage updates. Michal Soltys (18): drivers/apcsmart.h: comsetics - adjust tabs and add basic modeline drivers/apcsmart.h: add compat hardware, add commands apcsmart: remove APC_IGNORE drivers/apcsmart.c: cosmetics drivers/apcsmart: adjust ignored chars and handle over/~over drivers/apcsmart.c: remove status_commit() from update_status() drivers/apcsmart: adjust how firmware_table_lookup() handles 'b' and 'V' drivers/dstate: add dstate_getflags() drivers/apcsmart: add APC_USERCTRL and APC_CRUCIAL drivers/apcsmart.c: add sdcmd_*() sdok() drivers/apcsmart.c: add upsdrv_shutdown_advanced() and upsdrv_shutdown_simple() drivers/apcsmart.c: adjust upsdrv_shutdown() drivers/apcsmart.c: update in-driver help information drivers/apcsmart.c: update version and authors drivers/apcsmart: remove CMD_IGN_CHARS drivers/apcsmart.c: don't overuse UPSDELAY drivers/apcsmart.c: add ser_flush_in() in setver_enum() and do_cmd() drivers/apcsmart: use STAT_INSTCMD_{HANDLED,FAILED} for sdcmd_*() drivers/apcsmart.c | 588 ++++++++++++++++++++++++++++++++++++++++------------ drivers/apcsmart.h | 82 ++++--- drivers/dstate.c | 5 + drivers/dstate.h | 1 + 4 files changed, 510 insertions(+), 166 deletions(-) -- 1.7.2.1
Michal Soltys
2011-Mar-05 10:38 UTC
[Nut-upsdev] [RFC apcsmart V3 01/18] drivers/apcsmart.h: comsetics - adjust tabs and add basic modeline
Signed-off-by: Michal Soltys <soltys at ziu.info> --- drivers/apcsmart.h | 28 +++++++++++++++------------- 1 files changed, 15 insertions(+), 13 deletions(-) diff --git a/drivers/apcsmart.h b/drivers/apcsmart.h index 41ebbe1..706bbad 100644 --- a/drivers/apcsmart.h +++ b/drivers/apcsmart.h @@ -260,28 +260,30 @@ struct { { "5ZM", "79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz/<>", 0 }, /* APC600 */ { "6QD", "79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, - { "6QI", "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 1250. */ { "8QD", "79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, { "8QI", "79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, - { "8TD", "79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, - { "8TI", "79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, + { "8TD", "79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, + { "8TI", "79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, /* CS 350 */ { "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 }, }; + +/* vim: set ts=8 sts=8 sw=8 noet: */ -- 1.7.2.1
Michal Soltys
2011-Mar-05 10:38 UTC
[Nut-upsdev] [RFC apcsmart V3 02/18] drivers/apcsmart.h: add compat hardware, add commands
- add few APC models to compatibility table - add shutdown.return.grace corresponding to '@' - add ups.firmware.old corresponding to 'V' - update table version Signed-off-by: Michal Soltys <soltys at ziu.info> --- drivers/apcsmart.h | 18 +++++++++++------- 1 files changed, 11 insertions(+), 7 deletions(-) diff --git a/drivers/apcsmart.h b/drivers/apcsmart.h index 706bbad..4cd0ad5 100644 --- a/drivers/apcsmart.h +++ b/drivers/apcsmart.h @@ -23,7 +23,7 @@ #include "serial.h" #include "timehead.h" -#define APC_TABLE_VERSION "version 2.1" +#define APC_TABLE_VERSION "version 2.2" /* Basic UPS reply line structure */ #define ENDCHAR 10 /* APC ends responses with LF */ @@ -109,15 +109,11 @@ typedef struct { apc_vartab_t apc_vartab[] = { + { "ups.firmware.old", 0, 'V' }, { "ups.firmware", 0, 'b' }, { "ups.firmware.aux", 0, 'v' }, { "ups.model", 0, 0x01 }, -/* FUTURE: depends on variable naming scheme */ -#if 0 - { "ups.model.code", 0, 'V' }, -#endif - { "ups.serial", 0, 'n' }, { "ups.mfr.date", 0, 'm' }, @@ -207,6 +203,7 @@ apc_vartab_t apc_vartab[] = { #define APC_CMD_CALTOGGLE 'D' #define APC_CMD_SHUTDOWN 'K' #define APC_CMD_SOFTDOWN 'S' +#define APC_CMD_GRACEDOWN '@' #define APC_CMD_SIMPWF 'U' #define APC_CMD_BTESTTOGGLE 'W' #define APC_CMD_OFF 'Z' @@ -232,6 +229,8 @@ apc_cmdtab_t apc_cmdtab[] { "test.battery.start", 0, APC_CMD_BTESTTOGGLE }, { "test.battery.stop", 0, APC_CMD_BTESTTOGGLE }, + { "shutdown.return.grace", + APC_NASTY, APC_CMD_GRACEDOWN }, { "shutdown.return", APC_NASTY, APC_CMD_SOFTDOWN }, { "shutdown.stayoff", APC_NASTY|APC_REPEAT, APC_CMD_SHUTDOWN }, @@ -268,7 +267,12 @@ struct { { "7QI", "79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, { "7TD", "79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, { "7TI", "79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, - /* SmartUPS 1250. */ + /* SmartUPS 900I */ + { "7II", "79ABCEFGKLMNOPQSUVWXYZcfg", 0 }, + /* SmartUPS 2000I */ + { "9II", "79ABCEFGKLMNOPQSUVWXYZcfg", 0 }, + { "9GI", "79ABCEFGKLMNOPQSUVWXYZcfg", 0 }, + /* SmartUPS 1250 */ { "8QD", "79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, { "8QI", "79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, { "8TD", "79ABCDEFGKLMNOPQRSUVWXYZcefgjklmnopqrsuxz", 0 }, -- 1.7.2.1
Michal Soltys
2011-Mar-05 10:38 UTC
[Nut-upsdev] [RFC apcsmart V3 03/18] apcsmart: remove APC_IGNORE
APC_IGNORE was only used in query_ups(), and could be easily handled by APC_PRESENT and different tests/sets. Also, APC_IGNORE's comment in .h was a bit misleading. Signed-off-by: Michal Soltys <soltys at ziu.info> --- drivers/apcsmart.c | 11 ++++++----- drivers/apcsmart.h | 1 - 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/drivers/apcsmart.c b/drivers/apcsmart.c index b3d482d..200df5d 100644 --- a/drivers/apcsmart.c +++ b/drivers/apcsmart.c @@ -252,8 +252,10 @@ static int query_ups(const char *var, int first) return 0; } - /* already known to not be supported? */ - if (vt->flags & APC_IGNORE) + /* + * not first run and already known to not be supported ? + */ + if (!first && !(vt->flags & APC_PRESENT)) return 0; /* empty the input buffer (while allowing the alert handler to run) */ @@ -278,11 +280,10 @@ static int query_ups(const char *var, int first) ser_comm_good(); - if ((ret < 1) || (!strcmp(temp, "NA"))) { /* not supported */ - vt->flags |= APC_IGNORE; + if ((ret < 1) || (!strcmp(temp, "NA"))) /* not supported */ return 0; - } + vt->flags |= APC_PRESENT; ptr = convert_data(vt, temp); dstate_setinfo(vt->name, "%s", ptr); diff --git a/drivers/apcsmart.h b/drivers/apcsmart.h index 4cd0ad5..95e8c14 100644 --- a/drivers/apcsmart.h +++ b/drivers/apcsmart.h @@ -77,7 +77,6 @@ /* Driver command table flag values */ #define APC_POLL 0x0001 /* Poll this variable regularly */ -#define APC_IGNORE 0x0002 /* Never poll this */ #define APC_PRESENT 0x0004 /* Capability seen on this UPS */ #define APC_RW 0x0010 /* read-write variable */ -- 1.7.2.1
Michal Soltys
2011-Mar-05 10:38 UTC
[Nut-upsdev] [RFC apcsmart V3 04/18] drivers/apcsmart.c: cosmetics
- prefer upsdebugx() over printf() (excluding shutdown for now) - add basic modeline for vim users - adjust upsdrv_initinfo() to avoid (null) output - minor other changes Signed-off-by: Michal Soltys <soltys at ziu.info> --- drivers/apcsmart.c | 37 ++++++++++++++++++++++--------------- 1 files changed, 22 insertions(+), 15 deletions(-) diff --git a/drivers/apcsmart.c b/drivers/apcsmart.c index 200df5d..929372f 100644 --- a/drivers/apcsmart.c +++ b/drivers/apcsmart.c @@ -73,7 +73,7 @@ static apc_vartab_t *vartab_lookup_name(const char *var) /* FUTURE: change to use function pointers */ /* convert APC formatting to NUT formatting */ -static const char *convert_data(apc_vartab_t *cmd_entry, char *upsval) +static const char *convert_data(apc_vartab_t *cmd_entry, const char *upsval) { static char tmp[128]; int tval; @@ -294,7 +294,7 @@ static void do_capabilities(void) { const char *ptr, *entptr; char upsloc, temp[512], cmd, loc, etmp[16], *endtemp; - int nument, entlen, i, matrix, ret; + int nument, entlen, i, matrix, ret, valid; apc_vartab_t *vt; upsdebugx(1, "APC - About to get capabilities string"); @@ -334,8 +334,8 @@ static void do_capabilities(void) endtemp = &temp[0] + strlen(temp); if (temp[0] != '#') { - printf("Unrecognized capability start char %c\n", temp[0]); - printf("Please report this error [%s]\n", temp); + upsdebugx(1, "Unrecognized capability start char %c", temp[0]); + upsdebugx(1, "Please report this error [%s]", temp); upslogx(LOG_ERR, "ERROR: unknown capability start char %c!", temp[0]); @@ -378,9 +378,10 @@ static void do_capabilities(void) entptr = &ptr[4]; vt = vartab_lookup_char(cmd); + valid = vt && ((loc == upsloc) || (loc == '4')); /* mark this as writable */ - if (vt && ((loc == upsloc) || (loc == '4'))) { + if (valid) { upsdebugx(1, "Supported capability: %02x (%c) - %s", cmd, loc, vt->name); @@ -391,11 +392,10 @@ static void do_capabilities(void) } for (i = 0; i < nument; i++) { - snprintf(etmp, entlen + 1, "%s", entptr); - - if (vt && ((loc == upsloc) || (loc == '4'))) - dstate_addenum(vt->name, "%s", - convert_data(vt, etmp)); + if (valid) { + snprintf(etmp, entlen + 1, "%s", entptr); + dstate_addenum(vt->name, "%s", convert_data(vt, etmp)); + } entptr += entlen; } @@ -1235,9 +1235,8 @@ void upsdrv_initups(void) cable = getval("cable"); - if (cable) - if (!strcasecmp(cable, ALT_CABLE_1)) - init_serial_0095B(); + if (cable && !strcasecmp(cable, ALT_CABLE_1)) + init_serial_0095B(); /* make sure we wake up if the UPS sends alert chars to us */ extrafd = upsfd; @@ -1257,6 +1256,8 @@ void upsdrv_help(void) void upsdrv_initinfo(void) { + const char *pmod, *pser; + if (!smartmode()) { fatalx(EXIT_FAILURE, "Unable to detect an APC Smart protocol UPS on port %s\n" @@ -1269,8 +1270,12 @@ void upsdrv_initinfo(void) getbaseinfo(); - printf("Detected %s [%s] on %s\n", dstate_getinfo("ups.model"), - dstate_getinfo("ups.serial"), device_path); + if (!(pmod = dstate_getinfo("ups.model"))) + pmod = "\"unknown model\""; + if (!(pser = dstate_getinfo("ups.serial"))) + pser = "unknown serial"; + + upsdebugx(1, "Detected %s [%s] on %s", pmod, pser, device_path); setuphandlers(); } @@ -1314,3 +1319,5 @@ void upsdrv_cleanup(void) ser_close(upsfd, device_path); } + +/* vim: set ts=8 sts=8 sw=8 noet: */ -- 1.7.2.1
Michal Soltys
2011-Mar-05 10:38 UTC
[Nut-upsdev] [RFC apcsmart V3 05/18] drivers/apcsmart: adjust ignored chars and handle over/~over
'*' is no longer ignored - this is old APC models' "OK" reply to some of the shutdown commands. '?' and '=' are responsible for alerting about overload (among supposedly other things), so we handle them now. Signed-off-by: Michal Soltys <soltys at ziu.info> --- drivers/apcsmart.c | 10 ++++++++++ drivers/apcsmart.h | 12 +++++++----- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/drivers/apcsmart.c b/drivers/apcsmart.c index 929372f..f0f1878 100644 --- a/drivers/apcsmart.c +++ b/drivers/apcsmart.c @@ -178,6 +178,16 @@ static void alert_handler(char ch) ups_status |= APC_STAT_RB; break; + case '?': /* set OVER */ + upsdebugx(4, "alert_handler: OVER"); + ups_status |= APC_STAT_OVER; + break; + + case '=': /* clear OVER */ + upsdebugx(4, "alert_handler: not OVER"); + ups_status &= ~APC_STAT_OVER; + break; + default: upsdebugx(4, "alert_handler got 0x%02x (unhandled)", ch); break; diff --git a/drivers/apcsmart.h b/drivers/apcsmart.h index 95e8c14..939b6cb 100644 --- a/drivers/apcsmart.h +++ b/drivers/apcsmart.h @@ -28,15 +28,17 @@ /* Basic UPS reply line structure */ #define ENDCHAR 10 /* APC ends responses with LF */ -/* these two are only used during startup */ -#define IGNCHARS "\015+$|!~%?=*#&" /* special characters to ignore */ +/* characters ignored by default */ +#define IGNCHARS "\015+$|!~%?=#&" /* special characters to ignore */ + +/* 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 */ /* normal polls: characters we don't want to parse (including a few alerts) */ -#define POLL_IGNORE "\015?=*&|" +#define POLL_IGNORE "\015&|" -/* alert characters we care about - OL, OB, LB, not LB, RB */ -#define POLL_ALERT "$!%+#" +/* alert characters we care about - OL, OB, LB, not LB, RB, OVER, not OVER */ +#define POLL_ALERT "$!%+#?=" #define UPSDELAY 50000 /* slow down multicharacter commands */ #define CMDLONGDELAY 1500000 /* some commands need a 1.5s gap for safety */ -- 1.7.2.1
Michal Soltys
2011-Mar-05 10:38 UTC
[Nut-upsdev] [RFC apcsmart V3 06/18] drivers/apcsmart.c: remove status_commit() from update_status()
Preceding call to ups_status_set() already handles it. Signed-off-by: Michal Soltys <soltys at ziu.info> --- drivers/apcsmart.c | 1 - 1 files changed, 0 insertions(+), 1 deletions(-) diff --git a/drivers/apcsmart.c b/drivers/apcsmart.c index f0f1878..917d261 100644 --- a/drivers/apcsmart.c +++ b/drivers/apcsmart.c @@ -441,7 +441,6 @@ static int update_status(void) ups_status = strtol(buf, 0, 16) & 0xff; ups_status_set(); - status_commit(); dstate_dataok(); return 1; -- 1.7.2.1
Michal Soltys
2011-Mar-05 10:38 UTC
[Nut-upsdev] [RFC apcsmart V3 07/18] drivers/apcsmart: adjust how firmware_table_lookup() handles 'b' and 'V'
As 'b' can return different things than firmware version on some old models, prefer 'V' over 'b'. Signed-off-by: Michal Soltys <soltys at ziu.info> --- drivers/apcsmart.c | 26 +++++++++++++++----------- 1 files changed, 15 insertions(+), 11 deletions(-) diff --git a/drivers/apcsmart.c b/drivers/apcsmart.c index 917d261..ef66fce 100644 --- a/drivers/apcsmart.c +++ b/drivers/apcsmart.c @@ -541,27 +541,28 @@ static int firmware_table_lookup(void) unsigned int i, j; char buf[SMALLBUF]; - upsdebugx(1, "Attempting firmware lookup"); + upsdebugx(1, "Attempting firmware lookup using command 'V'"); - ret = ser_send_char(upsfd, 'b'); + ret = ser_send_char(upsfd, 'V'); if (ret != 1) { - upslog_with_errno(LOG_ERR, "getbaseinfo: ser_send_char failed"); + upslog_with_errno(LOG_ERR, "firmware_table_lookup: ser_send_char failed"); return 0; } ret = ser_get_line(upsfd, buf, sizeof(buf), ENDCHAR, IGNCHARS, SER_WAIT_SEC, SER_WAIT_USEC); - /* see if this is an older version like an APC600 which doesn't - * response to 'a' or 'b' queries + /* + * Some UPSes support both 'V' and 'b'. As 'b' doesn't always return + * firmware version, we attempt that only if 'V' doesn't work. */ if ((ret < 1) || (!strcmp(buf, "NA"))) { - upsdebugx(1, "Attempting to contact older Smart-UPS version"); - ret = ser_send_char(upsfd, 'V'); + upsdebugx(1, "Attempting firmware lookup using command 'b'"); + ret = ser_send_char(upsfd, 'b'); if (ret != 1) { - upslog_with_errno(LOG_ERR, "getbaseinfo: ser_send_char failed"); + upslog_with_errno(LOG_ERR, "firmware_table_lookup: ser_send_char failed"); return 0; } @@ -572,10 +573,10 @@ static int firmware_table_lookup(void) upslog_with_errno(LOG_ERR, "firmware_table_lookup: ser_get_line failed"); return 0; } - - upsdebugx(2, "Firmware: [%s]", buf); } + upsdebugx(2, "Firmware: [%s]", buf); + /* this will be reworked if we get a lot of these things */ if (!strcmp(buf, "451.2.I")) { quirk_capability_overflow = 1; @@ -612,6 +613,10 @@ static void getbaseinfo(void) int ret = 0; 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) return; @@ -632,7 +637,6 @@ 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 */ oldapcsetup(); return; -- 1.7.2.1
Michal Soltys
2011-Mar-05 10:38 UTC
[Nut-upsdev] [RFC apcsmart V3 08/18] drivers/dstate: add dstate_getflags()
In cases such as immutable flag over readable value, we have to be able to acquire this info at driver level (to e.g. avoid polling, if user defined a variable with 'override' prefix). Signed-off-by: Michal Soltys <soltys at ziu.info> --- drivers/dstate.c | 5 +++++ drivers/dstate.h | 1 + 2 files changed, 6 insertions(+), 0 deletions(-) diff --git a/drivers/dstate.c b/drivers/dstate.c index a3009c5..b486687 100644 --- a/drivers/dstate.c +++ b/drivers/dstate.c @@ -696,6 +696,11 @@ const char *dstate_getinfo(const char *var) return state_getinfo(dtree_root, var); } +int dstate_getflags(const char *var) +{ + return state_getflags(dtree_root, var); +} + void dstate_addcmd(const char *cmdname) { int ret; diff --git a/drivers/dstate.h b/drivers/dstate.h index 7ffe899..19f50a1 100644 --- a/drivers/dstate.h +++ b/drivers/dstate.h @@ -48,6 +48,7 @@ int dstate_addenum(const char *var, const char *fmt, ...) void dstate_setflags(const char *var, int flags); void dstate_setaux(const char *var, int aux); const char *dstate_getinfo(const char *var); +int dstate_getflags(const char *var); void dstate_addcmd(const char *cmdname); int dstate_delinfo(const char *var); int dstate_delenum(const char *var, const char *val); -- 1.7.2.1
Michal Soltys
2011-Mar-05 10:38 UTC
[Nut-upsdev] [RFC apcsmart V3 09/18] drivers/apcsmart: add APC_USERCTRL and APC_CRUCIAL
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
Michal Soltys
2011-Mar-05 10:38 UTC
[Nut-upsdev] [RFC apcsmart V3 10/18] drivers/apcsmart.c: add sdcmd_*() sdok()
Add functions that will be used for improved shutdown controls in next patches. Signed-off-by: Michal Soltys <soltys at ziu.info> --- drivers/apcsmart.c | 132 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 132 insertions(+), 0 deletions(-) diff --git a/drivers/apcsmart.c b/drivers/apcsmart.c index bb8d394..eb261d9 100644 --- a/drivers/apcsmart.c +++ b/drivers/apcsmart.c @@ -826,6 +826,138 @@ static int smartmode(void) return 0; /* failure */ } +/* + * all shutdown commands should respond with 'OK' or '*' + */ +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); + + if (!strcmp(temp, "*") || !strcmp(temp, "OK")) { + upsdebugx(4, "Last issued shutdown command succeeded"); + return 1; + } + + upsdebugx(1, "Last issued shutdown command failed"); + return 0; +} + +/* soft hibernate: S - working only when OB, otherwise ignored */ +static int sdcmd_S(int dummy) +{ + ser_flush_in(upsfd, IGNCHARS, nut_debug_level); + + upsdebugx(1, "Issuing soft hibernate"); + ser_send_char(upsfd, APC_CMD_SOFTDOWN); + + return sdok(); +} + +/* soft hibernate, hack version for CS 350 */ +static int sdcmd_CS(int tval) +{ + upsdebugx(1, "Using CS 350 'force OB' shutdown method"); + if (tval & APC_STAT_OL) { + upsdebugx(1, "On-line - forcing OB temporarily"); + ser_send_char(upsfd, 'U'); + usleep(UPSDELAY); + } + return sdcmd_S(tval); +} + +/* + * hard hibernate: @nnn / @nn + * note: works differently for older and new models, see help function for + * detailed info + */ +static int sdcmd_ATn(int cnt) +{ + int n = 0, mmax, ret; + const char *strval; + char timer[4]; + + mmax = cnt == 2 ? 99 : 999; + + if ((strval = getval("wugrace"))) { + errno = 0; + n = strtol(strval, NULL, 10); + if (errno || n < 0 || n > mmax) + n = 0; + } + + snprintf(timer, sizeof(timer), "%.*d", cnt, n); + + ser_flush_in(upsfd, IGNCHARS, nut_debug_level); + 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, timer); + + ret = sdok(); + if (ret || cnt == 3) + return ret; + + /* + * "tricky" part - we tried @nn variation and it (unsurprisingly) + * failed; we have to abort the sequence with something bogus to have + * the clean state; newer upses will respond with 'NO', older will be + * silent (YMMV); + */ + ser_send_char(upsfd, APC_CMD_GRACEDOWN); + usleep(UPSDELAY); + ser_flush_in(upsfd, IGNCHARS, nut_debug_level); + + return 0; +} + +/* shutdown: K - delayed poweroff */ +static int sdcmd_K(int dummy) +{ + ser_flush_in(upsfd, IGNCHARS, nut_debug_level); + upsdebugx(1, "Issuing delayed poweroff"); + + ser_send_char(upsfd, APC_CMD_SHUTDOWN); + usleep(CMDLONGDELAY); + ser_send_char(upsfd, APC_CMD_SHUTDOWN); + + return sdok(); +} + +/* shutdown: Z - immediate poweroff */ +static int sdcmd_Z(int dummy) +{ + ser_flush_in(upsfd, IGNCHARS, nut_debug_level); + upsdebugx(1, "Issuing immediate poweroff"); + + ser_send_char(upsfd, APC_CMD_OFF); + usleep(CMDLONGDELAY); + ser_send_char(upsfd, APC_CMD_OFF); + + return sdok(); +} + +static int (*sdlist[])(int) = { + sdcmd_S, + sdcmd_ATn, /* for @nnn version */ + sdcmd_K, + sdcmd_Z, + sdcmd_CS, + sdcmd_ATn, /* for @nn version */ +}; + +#define SDIDX_S 0 +#define SDIDX_AT3N 1 +#define SDIDX_K 2 +#define SDIDX_Z 3 +#define SDIDX_CS 4 +#define SDIDX_AT2N 5 + +#define SDCNT 6 + /* power down the attached load immediately */ void upsdrv_shutdown(void) { -- 1.7.2.1
Michal Soltys
2011-Mar-05 10:38 UTC
[Nut-upsdev] [RFC apcsmart V3 11/18] drivers/apcsmart.c: add upsdrv_shutdown_advanced() and upsdrv_shutdown_simple()
Add functions for simple and advanced shutdown control. Signed-off-by: Michal Soltys <soltys at ziu.info> --- drivers/apcsmart.c | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 files changed, 100 insertions(+), 0 deletions(-) diff --git a/drivers/apcsmart.c b/drivers/apcsmart.c index eb261d9..3ebbc50 100644 --- a/drivers/apcsmart.c +++ b/drivers/apcsmart.c @@ -958,6 +958,106 @@ static int (*sdlist[])(int) = { #define SDCNT 6 +static void upsdrv_shutdown_simple(int status) +{ + unsigned int sdtype = 0; + char *strval; + + if ((strval = getval("sdtype"))) { + errno = 0; + sdtype = strtol(strval, NULL, 10); + if (errno || sdtype < 0 || sdtype > 6) + sdtype = 0; + } + + switch (sdtype) { + + case 6: /* hard hibernate */ + sdcmd_ATn(3); + break; + case 5: /* "hack nn" hard hibernate */ + sdcmd_ATn(2); + break; + case 4: /* special hack for CS 350 and similar models */ + sdcmd_CS(status); + break; + + case 3: /* delayed poweroff */ + sdcmd_K(0); + break; + + case 2: /* instant poweroff */ + sdcmd_Z(0); + break; + case 1: + /* + * Send a combined set of shutdown commands which can work + * better if the UPS gets power during shutdown process + * Specifically it sends both the soft shutdown 'S' and the + * hard hibernate '@nnn' commands + */ + upsdebugx(1, "UPS - currently %s - sending soft/hard hibernate commands", + (status & APC_STAT_OL) ? "on-line" : "on battery"); + + /* S works only when OB */ + if ((status & APC_STAT_OB) && sdcmd_S(0)) + break; + sdcmd_ATn(3); + break; + + default: + /* + * Send @nnn or S, depending on OB / OL status + */ + if (status & APC_STAT_OL) /* on line */ + sdcmd_ATn(3); + else + sdcmd_S(0); + } +} + +static void upsdrv_shutdown_advanced(int status) +{ + const char *strval; + const char deforder[] = {48 + SDIDX_S, + 48 + SDIDX_AT3N, + 48 + SDIDX_K, + 48 + SDIDX_Z, + 0}; + size_t i; + int n; + + strval = getval("advorder"); + + /* sanitize advorder */ + + if (!strval || !strlen(strval) || strlen(strval) > SDCNT) + strval = deforder; + for (i = 0; i < strlen(strval); i++) { + if (strval[i] - 48 < 0 || strval[i] - 48 >= SDCNT) { + strval = deforder; + break; + } + } + + /* + * try each method in the list with a little bit of handling in certain + * cases + */ + + 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; + } + if (sdlist[strval[i] - 48](n)) + break; /* finish if command succeeded */ + } +} + /* power down the attached load immediately */ void upsdrv_shutdown(void) { -- 1.7.2.1
Michal Soltys
2011-Mar-05 10:38 UTC
[Nut-upsdev] [RFC apcsmart V3 12/18] drivers/apcsmart.c: adjust upsdrv_shutdown()
Adjust upsdrv_shutdown() to use upsdrv_shutdown_advanced() and upsdrv_shutdown_simple() functions. Signed-off-by: Michal Soltys <soltys at ziu.info> --- drivers/apcsmart.c | 86 +++++++-------------------------------------------- 1 files changed, 12 insertions(+), 74 deletions(-) diff --git a/drivers/apcsmart.c b/drivers/apcsmart.c index 3ebbc50..e8ae00a 100644 --- a/drivers/apcsmart.c +++ b/drivers/apcsmart.c @@ -1062,97 +1062,35 @@ static void upsdrv_shutdown_advanced(int status) void upsdrv_shutdown(void) { char temp[32]; - int ret, tval, sdtype = 0; + int ret, status; if (!smartmode()) - printf("Detection failed. Trying a shutdown command anyway.\n"); + upsdebugx(1, "SM detection failed. Trying a shutdown command anyway"); /* check the line status */ ret = ser_send_char(upsfd, APC_STATUS); if (ret == 1) { - ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, + ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, IGNCHARS, SER_WAIT_SEC, SER_WAIT_USEC); if (ret < 1) { - printf("Status read failed! Assuming on battery state\n"); - tval = APC_STAT_LB | APC_STAT_OB; + upsdebugx(1, "Status read failed ! Assuming on battery state"); + status = APC_STAT_LB | APC_STAT_OB; } else { - tval = strtol(temp, 0, 16); + status = strtol(temp, 0, 16); } } else { - printf("Status request failed; assuming on battery state\n"); - tval = APC_STAT_LB | APC_STAT_OB; + upsdebugx(1, "Status request failed; assuming on battery state"); + status = APC_STAT_LB | APC_STAT_OB; } - if (testvar("sdtype")) - sdtype = atoi(getval("sdtype")); - - switch (sdtype) { - - case 4: /* special hack for CS 350 and similar models */ - printf("Using CS 350 'force OB' shutdown method\n"); - - if (tval & APC_STAT_OL) { - printf("On line - forcing OB temporarily\n"); - ser_send_char(upsfd, 'U'); - } - - ser_send_char(upsfd, 'S'); - break; - - case 3: /* shutdown with grace period */ - printf("Sending delayed power off command to UPS\n"); - - ser_send_char(upsfd, APC_CMD_SHUTDOWN); - usleep(CMDLONGDELAY); - ser_send_char(upsfd, APC_CMD_SHUTDOWN); - - break; - - case 2: /* instant shutdown */ - printf("Sending power off command to UPS\n"); - - ser_send_char(upsfd, APC_CMD_OFF); - usleep(CMDLONGDELAY); - ser_send_char(upsfd, APC_CMD_OFF); - - break; - - case 1: - - /* Send a combined set of shutdown commands which can work better */ - /* if the UPS gets power during shutdown process */ - /* Specifically it sends both the soft shutdown 'S' */ - /* and the powerdown after grace period - '@000' commands */ - printf("UPS - currently %s - sending shutdown/powerdown\n", - (tval & APC_STAT_OL) ? "on-line" : "on battery"); - - ser_flush_in(upsfd, IGNCHARS, nut_debug_level); - ser_send_pace(upsfd, UPSDELAY, "S at 000"); - break; - - default: - - /* @000 - shutdown after 'p' grace period */ - /* - returns after 000 minutes (i.e. right away) */ - - /* S - shutdown after 'p' grace period, only on battery */ - /* returns after 'e' charge % plus 'r' seconds */ - - ser_flush_in(upsfd, IGNCHARS, nut_debug_level); - - if (tval & APC_STAT_OL) { /* on line */ - printf("On line, sending shutdown+return command...\n"); - ser_send_pace(upsfd, UPSDELAY, "@000"); - } - else { - printf("On battery, sending normal shutdown command...\n"); - ser_send_char(upsfd, APC_CMD_SOFTDOWN); - } - } + if (testvar("advorder") && strcasecmp(getval("advorder"), "no")) + upsdrv_shutdown_advanced(status); + else + upsdrv_shutdown_simple(status); } /* 940-0095B support: set DTR, lower RTS */ -- 1.7.2.1
Michal Soltys
2011-Mar-05 10:38 UTC
[Nut-upsdev] [RFC apcsmart V3 13/18] drivers/apcsmart.c: update in-driver help information
Signed-off-by: Michal Soltys <soltys at ziu.info> --- drivers/apcsmart.c | 93 +++++++++++++++++++++++++++++++++++++++++++++++----- 1 files changed, 84 insertions(+), 9 deletions(-) diff --git a/drivers/apcsmart.c b/drivers/apcsmart.c index e8ae00a..ac459de 100644 --- a/drivers/apcsmart.c +++ b/drivers/apcsmart.c @@ -1444,7 +1444,9 @@ static void setuphandlers(void) void upsdrv_makevartable(void) { addvar(VAR_VALUE, "cable", "Specify alternate cable (940-0095B)"); - addvar(VAR_VALUE, "sdtype", "Specify shutdown type (1-3)"); + addvar(VAR_VALUE, "wugrace", "Hard hibernate's wakeup grace"); + addvar(VAR_VALUE, "sdtype", "Specify simple shutdown method (0-6)"); + addvar(VAR_VALUE, "advorder", "Enable advanced shutdown control"); } void upsdrv_initups(void) @@ -1465,14 +1467,87 @@ void upsdrv_initups(void) void upsdrv_help(void) { - printf("\nShutdown types:\n"); - printf(" 0: soft shutdown or powerdown, depending on battery status\n"); - printf(" 1: soft shutdown followed by powerdown\n"); - printf(" 2: instant power off\n"); - printf(" 3: power off with grace period\n"); - printf(" 4: 'force OB' hack method for CS 350\n"); - printf("Modes 0-1 will make the UPS come back when power returns\n"); - printf("Modes 2-3 will make the UPS stay turned off when power returns\n"); + printf( + "\n\nAdditional explanation of the driver's options:\n\n" + + " sdtype:\n" + " see \"Simple shutdown method\" below for details\n\n" + + " advorder:\n" + " see \"Advanced shutdown control\" below for details\n\n" + + " wugrace:\n" + " Additional grace period used with 'hard hibernate' shutdown command.\n" + " The value is in 6 minute units and its acceptable range is 0 - 999.\n" + " If the value is invalid or out of range, it's assumed to be 0.\n" + " \"nn hack\" version of the command expects 0 - 99 range.\n\n" + + "Shutdown types:\n\n" + + " soft hibernate:\n" + " Works only when the ups is in OB state. The power is cut off after the\n" + " eeprom defined grace period. The ups will wake up when the power\n" + " returns, after the eeprom defined delay AND if the eeprom defined min.\n" + " battery charge level is met. The delay is counted from the power's\n" + " return.\n\n" + + " On older models (usually w/o programmable eeprom), the ups will power up\n" + " immediately after the power returns. On such models, it's safer to use\n" + " 'hard hibernate'. YMMV, depending on the ups model and firmware\n" + " revision.\n\n" + + " hard hibernate:\n" + " Works regardless if the ups is in OB or OL states. The power is cut off\n" + " after the eeprom defined grace period. The ups will wake up when the\n" + " power returns, after the eeprom defined delay + 6*n AND if the eeprom\n" + " defined min. battery charge level is met. The delay is counted from the\n" + " power's return. Value 'n' is in 6 minute units, and can be provided by\n" + " the user.\n\n" + + " On older models (usually w/o programmable eeprom), the ups will power up\n" + " after 6*n minutes, often regardless it the power returned on not. YMMV,\n" + " depending on the ups model and firmware revision.\n\n" + + " delayed poweroff:\n" + " The ups will powerdown after the eeprom defined grace period. The ups\n" + " stays offline until the user's intervention.\n\n" + + " instant poweroff:\n" + " The ups will powerdown immediately. The ups stays offline until the\n" + " user's intervention.\n\n" + + " CS 350 hack:\n" + " The same as 'soft hibernate', but first the ups is forced to go into OB\n" + " state.\n\n" + + "Simple shutdown method:\n\n" + + " 0: soft hibernate or hard hibernate, depending on battery status\n" + " 1: soft hibernate followed by hard hibernate, if the former fails\n" + " 2: instant poweroff\n" + " 3: delayed poweroff\n" + " 4: \"force OB\" hack method for CS 350\n" + " 5: \"hack nn\" hard hibernate only\n" + " 6: hard hibernate only\n\n" + + " User should provide requested method in 'sdtype'. The default is 0.\n\n" + + "Advanced shutdown control:\n\n" + + " 0: soft hibernate\n" + " 1: hard hibernate\n" + " 2: delayed poweroff\n" + " 3: instant poweroff\n" + " 4: \"force OB\" hack method for CS 350\n" + " 5: \"nn hack\" hard hibernate\n\n" + + " User should set the 'advorder' option and provide the list of the methods.\n" + " The methods are tried in order, until one of them succeedes.\n" + " If the list is too long or contains invalid characters, it will fallback to\n" + " the default - 0123. You can also use \"no\" to explicitly ignore it and use\n" + " \"sdtype\". Advanced shutdown control takes precedence over simple\n" + " one, if both are defined.\n" + ); } void upsdrv_initinfo(void) -- 1.7.2.1
Michal Soltys
2011-Mar-05 10:38 UTC
[Nut-upsdev] [RFC apcsmart V3 14/18] drivers/apcsmart.c: update version and authors
Signed-off-by: Michal Soltys <soltys at ziu.info> --- drivers/apcsmart.c | 7 ++++--- 1 files changed, 4 insertions(+), 3 deletions(-) diff --git a/drivers/apcsmart.c b/drivers/apcsmart.c index ac459de..d080a7d 100644 --- a/drivers/apcsmart.c +++ b/drivers/apcsmart.c @@ -24,7 +24,7 @@ #include "apcsmart.h" #define DRIVER_NAME "APC Smart protocol driver" -#define DRIVER_VERSION "2.03" +#define DRIVER_VERSION "2.1" static upsdrv_info_t table_info = { "APC command table", @@ -38,8 +38,9 @@ static upsdrv_info_t table_info = { upsdrv_info_t upsdrv_info = { DRIVER_NAME, DRIVER_VERSION, - "Russell Kroll <rkroll at exploits.org>\n" \ - "Nigel Metheringham <Nigel.Metheringham at Intechnology.co.uk>", + "Russell Kroll <rkroll at exploits.org>\n" + "Nigel Metheringham <Nigel.Metheringham at Intechnology.co.uk>\n" + "Michal Soltys <soltys at ziu.info>", DRV_STABLE, { &table_info, NULL } }; -- 1.7.2.1
Michal Soltys
2011-Mar-05 10:38 UTC
[Nut-upsdev] [RFC apcsmart V3 15/18] drivers/apcsmart: remove CMD_IGN_CHARS
protocol_verify() only cares about explicitly specified commands and variables. Signed-off-by: Michal Soltys <soltys at ziu.info> --- drivers/apcsmart.c | 4 ---- drivers/apcsmart.h | 3 --- 2 files changed, 0 insertions(+), 7 deletions(-) diff --git a/drivers/apcsmart.c b/drivers/apcsmart.c index d080a7d..0a6a56b 100644 --- a/drivers/apcsmart.c +++ b/drivers/apcsmart.c @@ -485,10 +485,6 @@ static void protocol_verify(unsigned char cmd) { int i, found, flags; - /* we might not care about this one */ - if (strchr(CMD_IGN_CHARS, cmd)) - return; - /* see if it's a variable */ for (i = 0; apc_vartab[i].name != NULL; i++) { diff --git a/drivers/apcsmart.h b/drivers/apcsmart.h index c85f76b..9862326 100644 --- a/drivers/apcsmart.h +++ b/drivers/apcsmart.h @@ -249,9 +249,6 @@ apc_cmdtab_t apc_cmdtab[] { NULL, 0, 0 } }; -/* things to ignore in protocol_verify - useless variables, etc. */ -#define CMD_IGN_CHARS "\032-78 at .,~\047\177QHRTYayz)1IJ" - /* compatibility with hardware that doesn't do APC_CMDSET ('a') */ struct { -- 1.7.2.1
Michal Soltys
2011-Mar-05 10:38 UTC
[Nut-upsdev] [RFC apcsmart V3 16/18] drivers/apcsmart.c: don't overuse UPSDELAY
This delay is meant for multicharacter commands, besides we always guarantee delay by other means - so do the stuff properly. Signed-off-by: Michal Soltys <soltys at ziu.info> --- drivers/apcsmart.c | 17 +++++++++-------- 1 files changed, 9 insertions(+), 8 deletions(-) diff --git a/drivers/apcsmart.c b/drivers/apcsmart.c index 0a6a56b..63b91b5 100644 --- a/drivers/apcsmart.c +++ b/drivers/apcsmart.c @@ -854,13 +854,15 @@ 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); + ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, IGNCHARS, SER_WAIT_SEC, SER_WAIT_USEC); } return sdcmd_S(tval); } @@ -874,7 +876,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; @@ -885,14 +887,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); 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, timer); + ser_send_pace(upsfd, UPSDELAY, temp); ret = sdok(); if (ret || cnt == 3) @@ -905,8 +907,7 @@ 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); + ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, IGNCHARS, SER_WAIT_SEC, SER_WAIT_USEC); return 0; } -- 1.7.2.1
Michal Soltys
2011-Mar-05 10:38 UTC
[Nut-upsdev] [RFC apcsmart V3 17/18] drivers/apcsmart.c: add ser_flush_in() in setver_enum() and do_cmd()
Just in case, and for the sake of consistency with setvar_string(). Signed-off-by: Michal Soltys <soltys at ziu.info> --- drivers/apcsmart.c | 3 ++- 1 files changed, 2 insertions(+), 1 deletions(-) diff --git a/drivers/apcsmart.c b/drivers/apcsmart.c index 63b91b5..70aebfc 100644 --- a/drivers/apcsmart.c +++ b/drivers/apcsmart.c @@ -1141,6 +1141,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); ret = ser_send_char(upsfd, vt->cmd); if (ret != 1) { @@ -1235,7 +1236,6 @@ static int setvar_string(apc_vartab_t *vt, const char *val) char temp[256]; ser_flush_in(upsfd, IGNCHARS, nut_debug_level); - ret = ser_send_char(upsfd, vt->cmd); if (ret != 1) { @@ -1338,6 +1338,7 @@ static int do_cmd(apc_cmdtab_t *ct) int ret; char buf[SMALLBUF]; + ser_flush_in(upsfd, IGNCHARS, nut_debug_level); ret = ser_send_char(upsfd, ct->cmd); if (ret != 1) { -- 1.7.2.1
Michal Soltys
2011-Mar-05 10:38 UTC
[Nut-upsdev] [RFC apcsmart V3 18/18] drivers/apcsmart: use STAT_INSTCMD_{HANDLED, FAILED} for sdcmd_*()
As we want the user to be able do issue all shutdown methods manually, adjust sdcmd_*() to use STAT_INSTCMD_{HANDLED,FAILED}, so they can be used directly. Also minor text corrections, and APC specific command renames. Signed-off-by: Michal Soltys <soltys at ziu.info> --- drivers/apcsmart.c | 75 ++++++++++++++++++++++++++++++++++------------------ drivers/apcsmart.h | 5 +++- 2 files changed, 53 insertions(+), 27 deletions(-) diff --git a/drivers/apcsmart.c b/drivers/apcsmart.c index 70aebfc..7f3c575 100644 --- a/drivers/apcsmart.c +++ b/drivers/apcsmart.c @@ -835,11 +835,11 @@ static int sdok(void) if (!strcmp(temp, "*") || !strcmp(temp, "OK")) { upsdebugx(4, "Last issued shutdown command succeeded"); - return 1; + return STAT_INSTCMD_HANDLED; } upsdebugx(1, "Last issued shutdown command failed"); - return 0; + return STAT_INSTCMD_FAILED; } /* soft hibernate: S - working only when OB, otherwise ignored */ @@ -864,7 +864,7 @@ static int sdcmd_CS(int status) ser_send_char(upsfd, 'U'); ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, IGNCHARS, SER_WAIT_SEC, SER_WAIT_USEC); } - return sdcmd_S(tval); + return sdcmd_S(0); } /* @@ -897,7 +897,7 @@ static int sdcmd_ATn(int cnt) ser_send_pace(upsfd, UPSDELAY, temp); ret = sdok(); - if (ret || cnt == 3) + if (ret == STAT_INSTCMD_HANDLED || cnt == 3) return ret; /* @@ -998,7 +998,7 @@ static void upsdrv_shutdown_simple(int status) (status & APC_STAT_OL) ? "on-line" : "on battery"); /* S works only when OB */ - if ((status & APC_STAT_OB) && sdcmd_S(0)) + if ((status & APC_STAT_OB) && sdcmd_S(0) == STAT_INSTCMD_HANDLED) break; sdcmd_ATn(3); break; @@ -1051,7 +1051,7 @@ static void upsdrv_shutdown_advanced(int status) } else if (strval[i] - 48 == SDIDX_AT2N) { n = 2; } - if (sdlist[strval[i] - 48](n)) + if (sdlist[strval[i] - 48](n) == STAT_INSTCMD_HANDLED) break; /* finish if command succeeded */ } } @@ -1343,7 +1343,7 @@ static int do_cmd(apc_cmdtab_t *ct) if (ret != 1) { upslog_with_errno(LOG_ERR, "do_cmd: ser_send_char failed"); - return STAT_INSTCMD_HANDLED; /* FUTURE: failed */ + return STAT_INSTCMD_FAILED; } /* some commands have to be sent twice with a 1.5s gap */ @@ -1354,24 +1354,24 @@ static int do_cmd(apc_cmdtab_t *ct) if (ret != 1) { upslog_with_errno(LOG_ERR, "do_cmd: ser_send_char failed"); - return STAT_INSTCMD_HANDLED; /* FUTURE: failed */ + return STAT_INSTCMD_FAILED; } } ret = read_buf(buf, sizeof(buf)); if (ret < 1) - return STAT_INSTCMD_HANDLED; /* FUTURE: failed */ + return STAT_INSTCMD_FAILED; - if (strcmp(buf, "OK") != 0) { + if (strcmp(buf, "OK") != 0 && strcmp(buf, "*") != 0) { upslogx(LOG_WARNING, "Got [%s] after command [%s]", buf, ct->name); - return STAT_INSTCMD_HANDLED; /* FUTURE: failed */ + return STAT_INSTCMD_FAILED; } upslogx(LOG_INFO, "Command: %s", ct->name); - return STAT_INSTCMD_HANDLED; /* FUTURE: success */ + return STAT_INSTCMD_HANDLED; } /* some commands must be repeated in a window to execute */ @@ -1390,10 +1390,10 @@ static int instcmd_chktime(apc_cmdtab_t *ct) if ((elapsed < MINCMDTIME) || (elapsed > MAXCMDTIME)) { upsdebugx(1, "instcmd_chktime: outside window for %s (%2.0f)", ct->name, elapsed); - return STAT_INSTCMD_HANDLED; /* FUTURE: again */ + return 0; } - return do_cmd(ct); + return 1; } static int instcmd(const char *cmdname, const char *extra) @@ -1424,10 +1424,33 @@ static int instcmd(const char *cmdname, const char *extra) if (!strcasecmp(cmdname, "calibrate.stop")) return do_cal(0); - if (ct->flags & APC_NASTY) - return instcmd_chktime(ct); + /* standard non-APC_NASTY command */ + if (!(ct->flags & APC_NASTY)) + return do_cmd(ct); + + /* APC_NASTY barrier */ + if (!instcmd_chktime(ct)) + return STAT_INSTCMD_FAILED; /* FUTURE: again */ + + if (!strcasecmp(cmdname, "load.off")) + return sdcmd_Z(0); + + if (!strcasecmp(cmdname, "shutdown.stayoff")) + return sdcmd_K(0); + + if (!strcasecmp(cmdname, "shutdown.return")) + return sdcmd_S(0); + + if (!strcasecmp(cmdname, "shutdown.return.cs")) + return sdcmd_CS(ups_status); + + if (!strcasecmp(cmdname, "shutdown.return.awd")) + return sdcmd_ATn(3); + + if (!strcasecmp(cmdname, "shutdown.return.awd.h")) + return sdcmd_ATn(2); - /* nothing special here */ + /* standard APC_NASTY command */ return do_cmd(ct); } @@ -1443,7 +1466,7 @@ static void setuphandlers(void) void upsdrv_makevartable(void) { addvar(VAR_VALUE, "cable", "Specify alternate cable (940-0095B)"); - addvar(VAR_VALUE, "wugrace", "Hard hibernate's wakeup grace"); + addvar(VAR_VALUE, "awd", "Additional wakeup delay for hard hibernate command"); addvar(VAR_VALUE, "sdtype", "Specify simple shutdown method (0-6)"); addvar(VAR_VALUE, "advorder", "Enable advanced shutdown control"); } @@ -1475,8 +1498,8 @@ void upsdrv_help(void) " advorder:\n" " see \"Advanced shutdown control\" below for details\n\n" - " wugrace:\n" - " Additional grace period used with 'hard hibernate' shutdown command.\n" + " awd:\n" + " Additional wakeup delay used with \"hard hibernate\" shutdown command.\n" " The value is in 6 minute units and its acceptable range is 0 - 999.\n" " If the value is invalid or out of range, it's assumed to be 0.\n" " \"nn hack\" version of the command expects 0 - 99 range.\n\n" @@ -1492,7 +1515,7 @@ void upsdrv_help(void) " On older models (usually w/o programmable eeprom), the ups will power up\n" " immediately after the power returns. On such models, it's safer to use\n" - " 'hard hibernate'. YMMV, depending on the ups model and firmware\n" + " \"hard hibernate\". YMMV, depending on the ups model and firmware\n" " revision.\n\n" " hard hibernate:\n" @@ -1500,8 +1523,8 @@ void upsdrv_help(void) " after the eeprom defined grace period. The ups will wake up when the\n" " power returns, after the eeprom defined delay + 6*n AND if the eeprom\n" " defined min. battery charge level is met. The delay is counted from the\n" - " power's return. Value 'n' is in 6 minute units, and can be provided by\n" - " the user.\n\n" + " power's return. Value \"n\" is in 6 minute units, and can be provided by\n" + " the user (see \"awd\" option).\n\n" " On older models (usually w/o programmable eeprom), the ups will power up\n" " after 6*n minutes, often regardless it the power returned on not. YMMV,\n" @@ -1516,7 +1539,7 @@ void upsdrv_help(void) " user's intervention.\n\n" " CS 350 hack:\n" - " The same as 'soft hibernate', but first the ups is forced to go into OB\n" + " The same as \"soft hibernate\", but first the ups is forced to go into OB\n" " state.\n\n" "Simple shutdown method:\n\n" @@ -1529,7 +1552,7 @@ void upsdrv_help(void) " 5: \"hack nn\" hard hibernate only\n" " 6: hard hibernate only\n\n" - " User should provide requested method in 'sdtype'. The default is 0.\n\n" + " User should provide requested method in \"sdtype\". The default is 0.\n\n" "Advanced shutdown control:\n\n" @@ -1540,7 +1563,7 @@ void upsdrv_help(void) " 4: \"force OB\" hack method for CS 350\n" " 5: \"nn hack\" hard hibernate\n\n" - " User should set the 'advorder' option and provide the list of the methods.\n" + " User should set the \"advorder\" option and provide the list of the methods.\n" " The methods are tried in order, until one of them succeedes.\n" " If the list is too long or contains invalid characters, it will fallback to\n" " the default - 0123. You can also use \"no\" to explicitly ignore it and use\n" diff --git a/drivers/apcsmart.h b/drivers/apcsmart.h index 9862326..6c4df52 100644 --- a/drivers/apcsmart.h +++ b/drivers/apcsmart.h @@ -235,9 +235,12 @@ apc_cmdtab_t apc_cmdtab[] { "test.battery.start", 0, APC_CMD_BTESTTOGGLE }, { "test.battery.stop", 0, APC_CMD_BTESTTOGGLE }, - { "shutdown.return.grace", + { "shutdown.return.awd",APC_NASTY, APC_CMD_GRACEDOWN }, + { "shutdown.return.awd.h", APC_NASTY, APC_CMD_GRACEDOWN }, { "shutdown.return", APC_NASTY, APC_CMD_SOFTDOWN }, + { "shutdown.return.cs", + APC_NASTY, APC_CMD_SOFTDOWN }, { "shutdown.stayoff", APC_NASTY|APC_REPEAT, APC_CMD_SHUTDOWN }, { "calibrate.start", 0, APC_CMD_CALTOGGLE }, -- 1.7.2.1
Arjen de Korte
2011-Mar-06 15:31 UTC
[Nut-upsdev] [RFC apcsmart V3 00/18] apcsmart driver updates
Citeren Michal Soltys <soltys op ziu.info>:> Follow up from previous thread: > > http://www.mail-archive.com/nut-upsdev op lists.alioth.debian.org/msg02331.html > > > Main differences is that V3 is split into many small patches, so the > reviewing > changes should be much easier. There're also a few additional > changes in later > commits.Thanks for these patches. One remark though, is that we prefer if you send patches as attachments rather than inline them. Inlining them means we have to painstakingly extract them from the messages (making sure the formatting is not changed along the way) and applying them in the hope we didn't miss a part. Attaching them means that we can simply save the attachment and apply that to the trunk immediately. Best regards, Arjen -- Please keep list traffic on the list (off-list replies will be rejected)