GitHub issues can also be used to report HCL updates
[networkupstools/kirr.git] / drivers / bestups.c
blob854bddf44087972e0b3358f251d8c238f3cacc9c
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
22 #include "main.h"
23 #include "serial.h"
25 #define DRIVER_NAME "Best UPS driver"
26 #define DRIVER_VERSION "1.06"
28 /* driver description structure */
29 upsdrv_info_t upsdrv_info = {
30 DRIVER_NAME,
31 DRIVER_VERSION,
32 "Russell Kroll <rkroll@exploits.org>\n" \
33 "Jason White <jdwhite@jdwhite.org>",
34 DRV_STABLE,
35 { NULL }
38 #define ENDCHAR 13 /* replies end with CR */
39 #define MAXTRIES 5
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);
54 return;
57 if (!strcmp(abbr, "FTC")) {
58 dstate_setinfo("ups.mfr", "%s", "Best Power");
59 dstate_setinfo("ups.model", "Fortress Telecom %s", rating);
60 return;
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;
67 return;
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;
74 return;
77 if (!strcmp(abbr, "325")) {
78 dstate_setinfo("ups.mfr", "%s", "Sola Australia");
79 dstate_setinfo("ups.model", "Sola 325 %s", rating);
80 return;
83 if (!strcmp(abbr, "520")) {
84 dstate_setinfo("ups.mfr", "%s", "Sola Australia");
85 dstate_setinfo("ups.model", "Sola 520 %s", rating);
86 return;
89 if (!strcmp(abbr, "610")) {
90 dstate_setinfo("ups.mfr", "%s", "Best Power");
91 dstate_setinfo("ups.model", "610 %s", rating);
92 return;
95 if (!strcmp(abbr, "620")) {
96 dstate_setinfo("ups.mfr", "%s", "Sola Australia");
97 dstate_setinfo("ups.model", "Sola 620 %s", rating);
98 return;
101 if (!strcmp(abbr, "AX1")) {
102 dstate_setinfo("ups.mfr", "%s", "Best Power");
103 dstate_setinfo("ups.model", "Axxium Rackmount %s", rating);
104 return;
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)
131 int i, ret;
132 char *ID;
134 ID = getval("ID"); /* user-supplied override from ups.conf */
136 if (ID) {
137 upsdebugx(2, "NOTE: using user-supplied ID response");
138 snprintf(buf, bufsize, "%s", ID);
139 return 1;
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);
148 if (ret > 0)
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) &&
153 (strlen(buf) <= 27))
154 return 1;
156 sleep(1);
159 upslogx(LOG_INFO, "Giving up on hardware detection after %d tries",
160 MAXTRIES);
162 return 0;
165 static void ups_ident(void)
167 int i;
168 char buf[256], *ptr;
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++) {
180 switch (i)
182 case 0:
183 model = ptr;
184 break;
186 case 1:
187 rating = ptr;
188 break;
190 case 2:
191 dstate_setinfo("input.voltage.nominal", "%d", atoi(ptr));
192 break;
194 case 3:
195 dstate_setinfo("output.voltage.nominal", "%d", atoi(ptr));
196 break;
198 case 4:
199 lowvolt = atof(ptr);
200 break;
202 case 5:
203 highvolt = atof(ptr);
204 break;
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");
219 if (ptr) {
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]);
229 break;
233 ptr = getval("nombattvolt");
235 if (ptr) {
236 highvolt = atof(ptr);
240 static void ups_sync(void)
242 char buf[256];
243 int i, ret;
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] == '('))
253 return;
255 usleep(250000);
258 fatalx(EXIT_FAILURE, "Unable to detect a Best/SOLA or Phoenix protocol UPS");
261 void upsdrv_initinfo(void)
263 ups_sync();
264 ups_ident();
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)
280 int i, ret;
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);
294 if (pstat[0] == '0')
295 return 1; /* on line */
297 return 0; /* on battery */
300 sleep(1);
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");
312 if (ups_on_line())
313 printf("The UPS will restart in about one minute.\n");
314 else
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];
324 float bvoltp;
325 int ret;
327 ret = ser_send_pace(upsfd, UPSDELAY, "\rQ1\r");
329 if (ret < 1) {
330 ser_comm_fail("ser_send_pace failed");
331 dstate_datastale();
332 return;
335 /* these things need a long time to respond completely */
336 usleep(200000);
338 ret = ser_get_line(upsfd, buf, sizeof(buf), ENDCHAR, "",
339 SER_WAIT_SEC, SER_WAIT_USEC);
341 if (ret < 1) {
342 ser_comm_fail("Poll failed: %s", ret ? strerror(errno) : "timeout");
343 dstate_datastale();
344 return;
347 if (ret < 46) {
348 ser_comm_fail("Poll failed: short read (got %d bytes)", ret);
349 dstate_datastale();
350 return;
353 if (ret > 46) {
354 ser_comm_fail("Poll failed: response too long (got %d bytes)",
355 ret);
356 dstate_datastale();
357 return;
360 if (buf[0] != '(') {
361 ser_comm_fail("Poll failed: invalid start character (got %02x)",
362 buf[0]);
363 dstate_datastale();
364 return;
367 ser_comm_good();
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);
375 if (bvoltp > 100) {
376 bvoltp = 100;
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);
391 status_init();
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))
401 status_set("BOOST");
403 if (atof(involt) > atof(outvolt))
404 status_set("TRIM");
407 } else {
408 status_set("OB"); /* on battery */
411 if (pstat[1] == '1')
412 status_set("LB"); /* low battery */
414 status_commit();
415 dstate_dataok();
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);