1 /* bestups.c - model specific routines for Best-UPS Fortress models
3 Copyright (C) 1999 Russell Kroll <rkroll@exploits.org>
5 ID config option by Jason White <jdwhite@jdwhite.org>
7 This program is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
12 This program is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 GNU General Public License for more details.
17 You should have received a copy of the GNU General Public License
18 along with this program; if not, write to the Free Software
19 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
25 #define DRIVER_NAME "Best UPS driver"
26 #define DRIVER_VERSION "1.06"
28 /* driver description structure */
29 upsdrv_info_t upsdrv_info
= {
32 "Russell Kroll <rkroll@exploits.org>\n" \
33 "Jason White <jdwhite@jdwhite.org>",
38 #define ENDCHAR 13 /* replies end with CR */
40 #define UPSDELAY 50000 /* 50 ms delay required for reliable operation */
42 #define SER_WAIT_SEC 3 /* allow 3.0 sec for ser_get calls */
43 #define SER_WAIT_USEC 0
45 static float lowvolt
= 0, highvolt
= 0;
46 static int battvoltmult
= 1;
47 static int inverted_bypass_bit
= 0;
49 static void model_set(const char *abbr
, const char *rating
)
51 if (!strcmp(abbr
, "FOR")) {
52 dstate_setinfo("ups.mfr", "%s", "Best Power");
53 dstate_setinfo("ups.model", "Fortress %s", rating
);
57 if (!strcmp(abbr
, "FTC")) {
58 dstate_setinfo("ups.mfr", "%s", "Best Power");
59 dstate_setinfo("ups.model", "Fortress Telecom %s", rating
);
63 if (!strcmp(abbr
, "PRO")) {
64 dstate_setinfo("ups.mfr", "%s", "Best Power");
65 dstate_setinfo("ups.model", "Patriot Pro %s", rating
);
66 inverted_bypass_bit
= 1;
70 if (!strcmp(abbr
, "PR2")) {
71 dstate_setinfo("ups.mfr", "%s", "Best Power");
72 dstate_setinfo("ups.model", "Patriot Pro II %s", rating
);
73 inverted_bypass_bit
= 1;
77 if (!strcmp(abbr
, "325")) {
78 dstate_setinfo("ups.mfr", "%s", "Sola Australia");
79 dstate_setinfo("ups.model", "Sola 325 %s", rating
);
83 if (!strcmp(abbr
, "520")) {
84 dstate_setinfo("ups.mfr", "%s", "Sola Australia");
85 dstate_setinfo("ups.model", "Sola 520 %s", rating
);
89 if (!strcmp(abbr
, "610")) {
90 dstate_setinfo("ups.mfr", "%s", "Best Power");
91 dstate_setinfo("ups.model", "610 %s", rating
);
95 if (!strcmp(abbr
, "620")) {
96 dstate_setinfo("ups.mfr", "%s", "Sola Australia");
97 dstate_setinfo("ups.model", "Sola 620 %s", rating
);
101 if (!strcmp(abbr
, "AX1")) {
102 dstate_setinfo("ups.mfr", "%s", "Best Power");
103 dstate_setinfo("ups.model", "Axxium Rackmount %s", rating
);
107 dstate_setinfo("ups.mfr", "%s", "Unknown");
108 dstate_setinfo("ups.model", "Unknown %s (%s)", abbr
, rating
);
110 printf("Unknown model detected - please report this ID: '%s'\n", abbr
);
113 static int instcmd(const char *cmdname
, const char *extra
)
115 if (!strcasecmp(cmdname
, "test.battery.stop")) {
116 ser_send_pace(upsfd
, UPSDELAY
, "CT\r");
117 return STAT_INSTCMD_HANDLED
;
120 if (!strcasecmp(cmdname
, "test.battery.start")) {
121 ser_send_pace(upsfd
, UPSDELAY
, "T\r");
122 return STAT_INSTCMD_HANDLED
;
125 upslogx(LOG_NOTICE
, "instcmd: unknown command [%s]", cmdname
);
126 return STAT_INSTCMD_UNKNOWN
;
129 static int get_ident(char *buf
, size_t bufsize
)
134 ID
= getval("ID"); /* user-supplied override from ups.conf */
137 upsdebugx(2, "NOTE: using user-supplied ID response");
138 snprintf(buf
, bufsize
, "%s", ID
);
142 for (i
= 0; i
< MAXTRIES
; i
++) {
143 ser_send_pace(upsfd
, UPSDELAY
, "\rID\r");
145 ret
= ser_get_line(upsfd
, buf
, bufsize
, ENDCHAR
, "",
146 SER_WAIT_SEC
, SER_WAIT_USEC
);
149 upsdebugx(2, "get_ident: got [%s]", buf
);
151 /* buf must start with ( and be in the range [25-27] */
152 if ((ret
> 0) && (buf
[0] != '(') && (strlen(buf
) >= 25) &&
159 upslogx(LOG_INFO
, "Giving up on hardware detection after %d tries",
165 static void ups_ident(void)
169 char *model
= NULL
, *rating
= NULL
;
171 if (!get_ident(buf
, sizeof(buf
))) {
172 fatalx(EXIT_FAILURE
, "Unable to detect a Best/SOLA or Phoenix protocol UPS");
175 /* FOR,750,120,120,20.0,27.6 */
176 ptr
= strtok(buf
, ",");
178 for (i
= 0; ptr
; i
++) {
191 dstate_setinfo("input.voltage.nominal", "%d", atoi(ptr
));
195 dstate_setinfo("output.voltage.nominal", "%d", atoi(ptr
));
203 highvolt
= atof(ptr
);
207 ptr
= strtok(NULL
, ",");
210 if ((!model
) || (!rating
)) {
211 fatalx(EXIT_FAILURE
, "Didn't get a valid ident string");
214 model_set(model
, rating
);
216 /* Battery voltage multiplier */
217 ptr
= getval("battvoltmult");
220 battvoltmult
= atoi(ptr
);
223 /* Lookup the nominal battery voltage (should be between lowvolt and highvolt */
224 for (i
= 0; i
< 8; i
++) {
225 const int nominal
[] = { 2, 6, 12, 24, 36, 48, 72, 96 };
227 if ((lowvolt
< nominal
[i
]) && (highvolt
> nominal
[i
])) {
228 dstate_setinfo("battery.voltage.nominal", "%d", battvoltmult
* nominal
[i
]);
233 ptr
= getval("nombattvolt");
236 highvolt
= atof(ptr
);
240 static void ups_sync(void)
245 for (i
= 0; i
< MAXTRIES
; i
++) {
246 ser_send_pace(upsfd
, UPSDELAY
, "\rQ1\r");
248 ret
= ser_get_line(upsfd
, buf
, sizeof(buf
), ENDCHAR
, "",
249 SER_WAIT_SEC
, SER_WAIT_USEC
);
251 /* return once we get something that looks usable */
252 if ((ret
> 0) && (buf
[0] == '('))
258 fatalx(EXIT_FAILURE
, "Unable to detect a Best/SOLA or Phoenix protocol UPS");
261 void upsdrv_initinfo(void)
266 printf("Detected %s %s on %s\n", dstate_getinfo("ups.mfr"),
267 dstate_getinfo("ups.model"), device_path
);
269 /* paranoia - cancel any shutdown that might already be running */
270 ser_send_pace(upsfd
, UPSDELAY
, "C\r");
272 upsh
.instcmd
= instcmd
;
274 dstate_addcmd("test.battery.start");
275 dstate_addcmd("test.battery.stop");
278 static int ups_on_line(void)
281 char temp
[256], pstat
[32];
283 for (i
= 0; i
< MAXTRIES
; i
++) {
284 ser_send_pace(upsfd
, UPSDELAY
, "\rQ1\r");
286 ret
= ser_get_line(upsfd
, temp
, sizeof(temp
), ENDCHAR
, "",
287 SER_WAIT_SEC
, SER_WAIT_USEC
);
289 /* Q1 must return 46 bytes starting with a ( */
290 if ((ret
> 0) && (temp
[0] == '(') && (strlen(temp
) == 46)) {
292 sscanf(temp
, "%*s %*s %*s %*s %*s %*s %*s %s", pstat
);
295 return 1; /* on line */
297 return 0; /* on battery */
303 upslogx(LOG_ERR
, "Status read failed: assuming on battery");
305 return 0; /* on battery */
308 void upsdrv_shutdown(void)
310 printf("The UPS will shut down in approximately one minute.\n");
313 printf("The UPS will restart in about one minute.\n");
315 printf("The UPS will restart when power returns.\n");
317 ser_send_pace(upsfd
, UPSDELAY
, "S01R0001\r");
320 void upsdrv_updateinfo(void)
322 char involt
[16], outvolt
[16], loadpct
[16], acfreq
[16],
323 battvolt
[16], upstemp
[16], pstat
[16], buf
[256];
327 ret
= ser_send_pace(upsfd
, UPSDELAY
, "\rQ1\r");
330 ser_comm_fail("ser_send_pace failed");
335 /* these things need a long time to respond completely */
338 ret
= ser_get_line(upsfd
, buf
, sizeof(buf
), ENDCHAR
, "",
339 SER_WAIT_SEC
, SER_WAIT_USEC
);
342 ser_comm_fail("Poll failed: %s", ret
? strerror(errno
) : "timeout");
348 ser_comm_fail("Poll failed: short read (got %d bytes)", ret
);
354 ser_comm_fail("Poll failed: response too long (got %d bytes)",
361 ser_comm_fail("Poll failed: invalid start character (got %02x)",
369 sscanf(buf
, "%*c%s %*s %s %s %s %s %s %s", involt
, outvolt
,
370 loadpct
, acfreq
, battvolt
, upstemp
, pstat
);
372 /* Guesstimation of battery charge left (inaccurate) */
373 bvoltp
= 100 * (atof(battvolt
) - lowvolt
) / (highvolt
- lowvolt
);
379 dstate_setinfo("battery.voltage", "%.1f", battvoltmult
* atof(battvolt
));
380 dstate_setinfo("input.voltage", "%s", involt
);
381 dstate_setinfo("output.voltage", "%s", outvolt
);
382 dstate_setinfo("ups.load", "%s", loadpct
);
383 dstate_setinfo("input.frequency", "%s", acfreq
);
385 if(upstemp
[0] != 'X') {
386 dstate_setinfo("ups.temperature", "%s", upstemp
);
389 dstate_setinfo("battery.charge", "%02.1f", bvoltp
);
393 if (pstat
[0] == '0') {
394 status_set("OL"); /* on line */
396 /* only allow these when OL since they're bogus when OB */
398 if (pstat
[2] == (inverted_bypass_bit
? '0' : '1')) {
399 /* boost or trim in effect */
400 if (atof(involt
) < atof(outvolt
))
403 if (atof(involt
) > atof(outvolt
))
408 status_set("OB"); /* on battery */
412 status_set("LB"); /* low battery */
418 void upsdrv_help(void)
422 void upsdrv_makevartable(void)
424 addvar(VAR_VALUE
, "nombattvolt", "Override nominal battery voltage");
425 addvar(VAR_VALUE
, "battvoltmult", "Battery voltage multiplier");
426 addvar(VAR_VALUE
, "ID", "Force UPS ID response string");
429 void upsdrv_initups(void)
431 upsfd
= ser_open(device_path
);
432 ser_set_speed(upsfd
, device_path
, B2400
);
435 void upsdrv_cleanup(void)
437 ser_close(upsfd
, device_path
);