Add functions that will be used for improved shutdown controls in next patches.
Signed-off-by: Michal Soltys <[email protected]> --- 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 _______________________________________________ Nut-upsdev mailing list [email protected] http://lists.alioth.debian.org/mailman/listinfo/nut-upsdev
