1
/* sms.c - driver for SMS UPS hardware
3
Copyright (C) 2001 Marcio Gomes <tecnica@microlink.com.br>
7
Copyright (C) 1999 Russell Kroll <rkroll@exploits.org>
9
This program is free software; you can redistribute it and/or modify
10
it under the terms of the GNU General Public License as published by
11
the Free Software Foundation; either version 2 of the License, or
12
(at your option) any later version.
14
This program is distributed in the hope that it will be useful,
15
but WITHOUT ANY WARRANTY; without even the implied warranty of
16
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17
GNU General Public License for more details.
19
You should have received a copy of the GNU General Public License
20
along with this program; if not, write to the Free Software
21
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
23
2001/05/17 - Version 0.10 - Initial release
24
2001/06/01 - Version 0.20 - Add Battery Informations in driver
25
2001/06/04 - Version 0.30 - Updated Battery Volts range, to reflect a correct
27
2002/12/02 - Version 0.40 - Update driver to new-model, based on Fentonups
29
2002/12/18 - Version 0.50 - Add Sinus Single 2 KVA in database, test with
30
new Manager III sinusoidal versions.
31
Change Detect Name, SMS do not pass real Models
32
in Megatec Info command, only the VA/KVA version
33
2002/12/18 - Version 0.51 - Updated Battery Volts range, to reflect a correct
35
2002/12/27 - Version 0.60 - Add new UPS Commands SDRET, SIMPWF change BTEST1
36
2002/12/28 - Version 0.70 - Add new UPS Commands SHUTDOWN,STOPSHUTD,WATCHDOG
37
2003/06/11 - Version 0.71 - Converted to dstate calls and names (rkroll)
40
Microlink ISP/Pop-Rio contributed with MANAGER III 1300, MANAGER III 650 UPS
41
and Sinus Single 2 KVA for my tests.
43
http://www.microlink.com.br and http://www.pop-rio.com.br
49
#define DRV_VERSION "0.71"
54
#define ENDCHAR 13 /* replies end with CR */
56
static int cap_upstemp = 0;
57
static float lowvolt = 0, voltrange;
58
static int lownorm, highnorm, poll_failures = 0;
60
void guessmodel(const char *raw)
66
printf ("0 1 2 3 \n");
67
printf ("012345678901234567890123567890123456789\n");
69
strlcpy(mstr, &raw[17], sizeof(mstr));
70
mstr[10] = '\0'; /* 10 chars max, per the protocol */
75
dstate_setinfo("ups.model", "SMS %s", mstr);
79
void getbaseinfo(void)
81
char temp[256], model[32], *raw;
84
/* dummy read attempt to sync - throw it out */
86
upsrecv(temp, sizeof(temp), ENDCHAR, "");
88
/* now retrieve information and parse */
90
upsrecv(temp, sizeof(temp), ENDCHAR, "");
94
fatalx("Bad UPS info start character [%s]", temp);
100
rtrim(&temp[1], ' ');
102
dstate_setinfo("ups.mfr", "%s", &temp[1]);
106
012345678901234567890123567890123456789
107
#SMS LTDA 1300 VA VER 1.0
108
#SMS LTDA 1300VA SEN VER 5.0
109
#SMS LTDA 2 KVA VER 1.0
111
/* grab full model string */
113
rtrim(&temp[17], ' ');
115
snprintf(model, sizeof(model), "%s", &temp[17]);
116
/* printf("->%s<-\n",model); */
119
/* figure out official model name and voltage info from table */
120
for (i = 0; modeltab[i].mtext != NULL; i++) {
121
if (!strcmp(modeltab[i].mtext, model)) {
123
lowvolt = modeltab[i].lowvolt;
124
voltrange = modeltab[i].voltrange;
125
cap_upstemp = modeltab[i].has_temp;
130
/* table lookup fails -> guess */
134
dstate_setinfo("ups.model", "%s", modeltab[modelnum].desc);
136
dstate_setinfo("input.transfer.low", "%i",
137
modeltab[modelnum].lowxfer);
138
dstate_setinfo("input.transfer.high", "%i",
139
modeltab[modelnum].highxfer);
141
lownorm = modeltab[modelnum].lownorm;
142
highnorm = modeltab[modelnum].highnorm;
145
/* now add instant command support info */
146
dstate_addcmd("test.battery.start");
147
dstate_addcmd("test.battery.stop");
148
dstate_addcmd("shutdown.return"); /* was CMD_SDRET */
149
dstate_addcmd("test.failure.start");
150
dstate_addcmd("shutdown.stayoff"); /* was CMD_SHUTDOWN */
151
dstate_addcmd("shutdown.stop");
152
dstate_addcmd("reset.watchdog");
154
printf("Detected %s on %s\n", dstate_getinfo("ups.model"), device_path);
157
/* paranoia - cancel any shutdown that might already be running */
161
int instcmd(const char *cmdname, const char *extra)
163
if (!strcasecmp(cmdname, "test.battery.stop")) {
165
return STAT_INSTCMD_HANDLED;
168
if (!strcasecmp(cmdname, "test.battery.start")) {
169
upssend("TL\r");/* start battery test until bat low */
170
return STAT_INSTCMD_HANDLED;
173
if (!strcasecmp(cmdname, "test.failure.start")) {
175
return STAT_INSTCMD_HANDLED;
178
if (!strcasecmp(cmdname, "shutdown.return")) {
179
/* shutdown and restart */
181
upssend("S.3R0003\r");
182
return STAT_INSTCMD_HANDLED;
185
if (!strcasecmp(cmdname, "shutdown.stayoff")) {
186
/* shutdown now (one way) */
189
return STAT_INSTCMD_HANDLED;
192
if (!strcasecmp(cmdname, "shutdown.stop")) {
193
/* Cancel Shutdown */
195
return STAT_INSTCMD_HANDLED;
198
if (!strcasecmp(cmdname, "reset.watchdog")) {
199
/* WATCHDOG Crontab Function */
201
upssend("S05R0003\r");
202
return STAT_INSTCMD_HANDLED;
205
upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname);
206
return STAT_INSTCMD_UNKNOWN;
209
void upsdrv_initinfo(void)
213
upsh.new_instcmd = instcmd;
214
dstate_setinfo("driver.version.internal", "%s", DRV_VERSION);
217
void pollfail(char *why)
221
/* don't spew into the syslog forever */
222
if (poll_failures < 3)
223
upslogx(LOG_ERR, why);
228
void upsdrv_updateinfo(void)
230
char temp[256], utility[16], loadpct[16], acfreq[16], battvolt[16],
231
upstemp[16], stat[16], outvolt[16];
237
ret = upsrecv (temp, sizeof(temp), ENDCHAR, "");
239
/* sanity checks for poll data */
240
if (strlen(temp) < 46) {
241
pollfail("Poll failed: short read from UPS");
246
if (strlen(temp) > 46) {
247
pollfail("Poll failed: oversized read from UPS");
252
if (temp[0] != '(') {
253
pollfail("Poll failed: invalid start character");
258
if (poll_failures > 0)
259
upslogx(LOG_NOTICE, "UPS poll succeeded");
263
/* (MMM.M NNN.N PPP.P QQQ RR.R S.SS TT.T b7b6b5b4b3b2b1b0<cr>
265
* MMM.M : input voltage (utility)
266
* NNN.N : fault voltage (ignored)
267
* PPP.P : output voltage
270
sscanf(temp, "%*c%s %*s %s %s %s %s %s %s", utility, outvolt, loadpct,
271
acfreq, battvolt, upstemp, stat);
273
dstate_setinfo("output.voltage", "%s", outvolt);
274
dstate_setinfo("input.voltage", "%s", utility);
275
dstate_setinfo("battery.voltage", "%s", battvolt);
277
bvoltp = ((atof(battvolt) - lowvolt) / voltrange) * 100.0;
282
dstate_setinfo("battery.charge", "%02.1f", bvoltp);
286
util = atoi(utility);
288
if (stat[0] == '0') {
289
status_set("OL"); /* on line */
291
/* only allow these when OL since they're bogus when OB */
292
if (stat[2] == '1') { /* boost or trim in effect */
301
status_set("OB"); /* on battery */
305
status_set("LB"); /* low battery */
309
if (cap_upstemp == 1)
310
dstate_setinfo("ups.temperature", "%s", upstemp);
312
dstate_setinfo("input.frequency", "%s", acfreq);
313
dstate_setinfo("ups.load", "%s", loadpct);
318
/* power down the attached load immediately */
319
void upsdrv_shutdown(void)
321
char temp[256], stat[32];
323
/* basic idea: find out line status and send appropriate command */
326
upsrecv (temp, sizeof(temp), ENDCHAR, "");
327
sscanf (temp, "%*s %*s %*s %*s %*s %*s %*s %s", stat);
329
/* on battery: send S01<cr>, ups will return by itself on utility */
330
/* on line: send S01R0003<cr>, ups will cycle and return soon */
334
if (stat[0] == '0') { /* on line */
335
printf("On line, sending shutdown+return command...\n");
338
printf("On battery, sending normal shutdown command...\n");
340
upssendchar(13); /* end sequence */
343
void upsdrv_help(void)
347
void upsdrv_makevartable(void)
351
void upsdrv_banner(void)
353
printf("Network UPS Tools - SMS UPS driver %s (%s)\n",
354
DRV_VERSION, UPS_VERSION);
355
printf("by Marcio Gomes at Microlink - tecnica@microlink.com.br\n\n");
358
void upsdrv_initups(void)
360
open_serial(device_path, B2400);
363
void upsdrv_cleanup(void)