1
/* cpsups.c - model specific routines for CyberPower text protocol UPSes
3
Copyright (C) 2003 Walt Holman <waltabbyh@comcast.net>
4
with thanks to Russell Kroll <rkroll@exploits.org>
6
This program is free software; you can redistribute it and/or modify
7
it under the terms of the GNU General Public License as published by
8
the Free Software Foundation; either version 2 of the License, or
9
(at your option) any later version.
11
This program is distributed in the hope that it will be useful,
12
but WITHOUT ANY WARRANTY; without even the implied warranty of
13
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
GNU General Public License for more details.
16
You should have received a copy of the GNU General Public License
17
along with this program; if not, write to the Free Software
18
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21
/* This driver started out as the bestups.c driver from 1.5.11 -
22
* I've hacked it up every which way to get it to function
23
* with a CPS1100AVR. Thanks go to the guys at
24
* http://networkupstools.org for creating a very nice toolset.
29
#define DRV_VERSION ".04"
31
static void model_set(const char *abbr, const char *rating)
35
* Added: Brad Sawatzky <brad+nut@lamorak.phys.virginia.edu> 02Jun04
36
* NOTE: I have no idea how to set the runtime parameter... I basically
37
* scaled up linearly from the 1100 and 500 entries based on the
38
* 'voltage'. The realtime runtime calculated under load looks
41
if (!strcmp(abbr, "#1500VA ")) {
42
dstate_setinfo("ups.mfr", "%s", "CyberPower");
43
dstate_setinfo("ups.model", "CPS1500AVR %s", rating);
44
dstate_setinfo("ups.runtime", "%s", "90");
45
dstate_setinfo("ups.power.nominal", "%s", "1500");
49
if (!strcmp(abbr, "#1100VA ")) {
50
dstate_setinfo("ups.mfr", "%s", "CyberPower");
51
dstate_setinfo("ups.model", "CPS1100VA %s", rating);
52
dstate_setinfo("ups.runtime", "%s", "60");
53
dstate_setinfo("ups.power.nominal", "%s", "1100");
57
/* Added: Armin Diehl <diehl@...> 14Dec04 */
58
if (!strcmp(abbr, "#1000VA ")) {
59
dstate_setinfo("ups.mfr", "%s", "MicroDowell");
60
dstate_setinfo("ups.model", "B.Box BP 1000 %s", rating);
61
dstate_setinfo("ups.runtime", "%s", "50");
62
dstate_setinfo("ups.voltage", "%s", "1000");
66
if (!strcmp(abbr, "#825VA ")) {
67
dstate_setinfo("ups.mfr", "%s", "CyberPower");
68
dstate_setinfo("ups.model", "CPS825VA %s", rating);
69
dstate_setinfo("ups.runtime", "%s", "29");
70
dstate_setinfo("ups.power.nominal", "%s", "825");
74
/* Added: Armin Diehl <diehl@...> 14Dec04 */
75
if (!strcmp(abbr, "#750VA ")) {
76
dstate_setinfo("ups.mfr", "%s", "MicroDowell");
77
dstate_setinfo("ups.model", "B.Box BP 750 %s", rating);
78
dstate_setinfo("ups.runtime", "%s", "29");
79
dstate_setinfo("ups.voltage", "%s", "825");
83
if (!strcmp(abbr, "#500VA ")) {
84
dstate_setinfo("ups.mfr", "%s", "CyberPower");
85
dstate_setinfo("ups.model", "OP500TE %s", rating);
86
dstate_setinfo("ups.runtime", "%s", "16.5");
87
dstate_setinfo("ups.power.nominal", "%s", "500");
91
dstate_setinfo("ups.mfr", "%s", "Unknown");
92
dstate_setinfo("ups.model", "Unknown %s (%s)", abbr, rating);
93
dstate_setinfo("ups.runtime", "%s", "1");
94
dstate_setinfo("ups.power.nominal", "%s", "1");
96
printf("Unknown model detected - please report this ID: '%s'\n", abbr);
99
static int instcmd(const char *cmdname, const char *extra)
101
/* The following commands also appear to be valid on the CPS1100 */
103
if (!strcasecmp(cmdname, "test.battery.stop")) {
104
ser_send_pace(upsfd, UPSDELAY, "CT\r");
105
return STAT_INSTCMD_HANDLED;
108
if (!strcasecmp(cmdname, "test.battery.start")) {
109
ser_send_pace(upsfd, UPSDELAY, "T\r");
110
return STAT_INSTCMD_HANDLED;
113
upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname);
114
return STAT_INSTCMD_UNKNOWN;
118
static int get_ident(char *buf, size_t bufsize)
122
for (i = 0; i < MAXTRIES; i++) {
123
ser_send_pace(upsfd, UPSDELAY, "\rP4\r");
125
ret = ser_get_line(upsfd, buf, bufsize, ENDCHAR, "",
126
SER_WAIT_SEC, SER_WAIT_USEC);
129
upsdebugx(2, "get_ident: got [%s]", buf);
131
/* buf must start with # and be in the range [25-27] */
132
if ((ret > 0) && (buf[0] == '#') && (strlen(buf) >= 25) &&
139
upslogx(LOG_INFO, "Giving up on hardware detection after %d tries",
145
static int scan_poll_values(char *buf)
147
char values[20][200], *pos;
148
int i = 0, battremain, length, rseconds;
151
/* These are used to hold status of UPS.
152
* val1 = online/onbattery status
154
char temp1=values[6][0];
157
while ((pollstatusmap[i].end != 0))
159
pos = &buf[pollstatusmap[i].begin];
160
length = pollstatusmap[i].end + 1 - pollstatusmap[i].begin;
161
strncpy(values[i],pos,length);
165
if ((*tmp1 & CPS_STAT_OL) && !(*tmp1 & CPS_STAT_OB))
168
if (*tmp1 & CPS_STAT_OB)
171
if (*tmp1 & CPS_STAT_CAL)
174
if (*tmp1 & CPS_STAT_LB)
182
battremain = strtol(dstate_getinfo("ups.power.nominal"),NULL,10) * (strtod(pos,NULL)/100);
184
/* Figure out runtime minutes */
185
rminutes = strtod(dstate_getinfo("ups.runtime"),NULL) *
186
((battremain * ( 1 - (strtod (values[2],NULL) / 100 ))) /
187
strtol(dstate_getinfo("ups.power.nominal"),NULL,10));
188
rseconds = ((int)(rminutes*100) - ((int)rminutes)*100) * 0.6 ;
190
dstate_setinfo("input.voltage", "%g", strtod(values[0],NULL));
191
dstate_setinfo("output.voltage", "%g", strtod(values[1],NULL));
192
dstate_setinfo("ups.load", "%li", strtol(values[2],NULL,10));
193
dstate_setinfo("input.frequency", "%g", strtod(values[5],NULL));
194
dstate_setinfo("ups.temperature", "%li", strtol(values[4],NULL,10));
195
dstate_setinfo("battery.charge", "%02.1f", strtod(values[3],NULL));
196
dstate_setinfo("battery.runtime", "%2.0f:%02d", rminutes, rseconds);
203
static void ups_ident(void)
205
char buf[256], *ptr, *com, *model, *rating;
208
if (!get_ident(buf, sizeof(buf)))
209
fatalx("Unable to detect a CyberPower text protocol UPS");
211
model = rating = NULL;
215
/* Leaving this in place for future */
216
for (i = 0; i < 2; i++) {
217
com = strchr(ptr, ',');
225
case 1: rating = ptr;
236
fatalx("Didn't get a valid ident string");
238
model_set(model, rating);
241
static void ups_sync(void)
246
for (i = 0; i < MAXTRIES; i++) {
247
ser_send_pace(upsfd, UPSDELAY, "\rP4\r");
248
upsdebugx(3, "ups_sync: send [%s]", "\\rP4\\r");
250
ret = ser_get_line(upsfd, buf, sizeof(buf), ENDCHAR, "",
251
SER_WAIT_SEC, SER_WAIT_USEC);
252
upsdebugx(3, "ups_sync: got ret %d [%s]", ret, buf);
254
/* return once we get something that looks usable */
255
if ((ret > 0) && (buf[0] == '#')) {
256
upsdebugx(3, "ups_sync: got line beginning with #, looks usable, returning");
263
fatalx("Unable to detect a CyberPower text protocol UPS");
266
void upsdrv_initinfo(void)
274
printf("Detected %s %s on %s\n", dstate_getinfo("ups.mfr"),
275
dstate_getinfo("ups.model"), device_path);
277
/* paranoia - cancel any shutdown that might already be running */
278
ser_send_pace(upsfd, UPSDELAY, "C\r"); /* Need a readback so the first poll doesn't fail */
279
ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, "", SER_WAIT_SEC, SER_WAIT_USEC);
281
upsh.instcmd = instcmd;
283
dstate_setinfo("driver.version.internal", "%s", DRV_VERSION);
284
dstate_addcmd("test.battery.start");
285
dstate_addcmd("test.battery.stop");
288
static int ups_on_line(void)
293
for (i = 0; i < MAXTRIES; i++) {
294
ser_send_pace(upsfd, UPSDELAY, "\rD\r");
296
ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, "",
297
SER_WAIT_SEC, SER_WAIT_USEC);
299
/* D must return 34 bytes starting with a # */
300
if ((ret > 0) && (temp[0] == '#') && (strlen(temp) == 34)) {
302
char * pos = &temp[pollstatusmap[POLL_UPSSTATUS].begin];
304
if ((*pos & CPS_STAT_OL) && !(*pos & CPS_STAT_OB))
305
return(1); /* on line */
307
return 0; /* on battery */
313
upslogx(LOG_ERR, "Status read failed: assuming on battery");
315
return 0; /* on battery */
318
void upsdrv_shutdown(void)
325
printf("The UPS will shut down in approximately one minute.\n");
328
printf("The UPS will restart in about one minute.\n");
330
printf("The UPS will restart when power returns.\n");
332
/* Although this is straight from the bestups.c driver, the UPS
333
* does indeed shutdown correctly. */
335
ser_send_pace(upsfd, UPSDELAY, "S01R0001\r");
337
ret = ser_get_line(upsfd, buf, sizeof(buf), ENDCHAR, "",
338
SER_WAIT_SEC, SER_WAIT_USEC);
340
if ((ret < 1) || (buf[0] != '#'))
341
printf ("Warning: got unexpected reply to shutdown command, shutdown may fail\n");
344
void upsdrv_updateinfo(void)
349
ret = ser_send_pace(upsfd, UPSDELAY, "\rD\r");
352
ser_comm_fail("ser_send_pace failed");
357
/* these things need a long time to respond completely */
360
ret = ser_get_line(upsfd, buf, sizeof(buf), ENDCHAR, "",
361
SER_WAIT_SEC, SER_WAIT_USEC);
370
if (ret == 2) /* We need to retry this read right away */
372
ret = ser_get_line(upsfd, buf, sizeof(buf), ENDCHAR, "", SER_WAIT_SEC, SER_WAIT_USEC);
374
ser_comm_fail("Poll failed: short read (got %d bytes)", ret);
379
ser_comm_fail("Poll failed: short read (got %d bytes)", ret);
386
upslogx(LOG_INFO, "String too long...");
387
ser_comm_fail("Poll failed: response too long (got %d bytes)",
394
ser_comm_fail("Poll failed: invalid start character (got %02x)",
402
scan_poll_values(buf);
408
void upsdrv_help(void)
412
void upsdrv_makevartable(void)
416
void upsdrv_banner(void)
418
printf("Network UPS Tools - CyberPower text protocol UPS driver %s (%s)\n",
419
DRV_VERSION, UPS_VERSION);
420
experimental_driver = 1; /* Causes a warning message to be printed */
423
void upsdrv_initups(void)
425
upsfd = ser_open(device_path);
426
ser_set_speed(upsfd, device_path, B2400);
430
void upsdrv_cleanup(void)
432
ser_close(upsfd, device_path);