~ubuntu-branches/ubuntu/saucy/nut/saucy

« back to all changes in this revision

Viewing changes to drivers/cpsups.c

  • Committer: Bazaar Package Importer
  • Author(s): Reinhard Tartler
  • Date: 2005-07-20 19:48:50 UTC
  • mto: (16.1.1 squeeze)
  • mto: This revision was merged to the branch mainline in revision 4.
  • Revision ID: james.westby@ubuntu.com-20050720194850-oo61wjr33rrx2mre
Tags: upstream-2.0.2
ImportĀ upstreamĀ versionĀ 2.0.2

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/* cpsups.c - model specific routines for CyberPower text protocol UPSes 
 
2
 
 
3
   Copyright (C) 2003  Walt Holman <waltabbyh@comcast.net>
 
4
   with thanks to Russell Kroll <rkroll@exploits.org>
 
5
 
 
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.
 
10
 
 
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.
 
15
 
 
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
 
19
*/
 
20
 
 
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.
 
25
*/
 
26
 
 
27
#include "cpsups.h"
 
28
 
 
29
#define DRV_VERSION ".04"
 
30
 
 
31
static void model_set(const char *abbr, const char *rating)
 
32
{
 
33
 
 
34
        /* 
 
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
 
39
         * reasonable.
 
40
         */
 
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");
 
46
                return;
 
47
        }
 
48
 
 
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");
 
54
                return;
 
55
        }
 
56
 
 
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");
 
63
                return;
 
64
        }
 
65
                
 
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");
 
71
                return;
 
72
        }
 
73
 
 
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");
 
80
                return;
 
81
        }
 
82
 
 
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");
 
88
                return;
 
89
        }
 
90
 
 
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");
 
95
 
 
96
        printf("Unknown model detected - please report this ID: '%s'\n", abbr);
 
97
}
 
98
 
 
99
static int instcmd(const char *cmdname, const char *extra)
 
100
{
 
101
        /* The following commands also appear to be valid on the CPS1100 */
 
102
 
 
103
        if (!strcasecmp(cmdname, "test.battery.stop")) {
 
104
                ser_send_pace(upsfd, UPSDELAY, "CT\r");
 
105
                return STAT_INSTCMD_HANDLED;
 
106
        }
 
107
 
 
108
        if (!strcasecmp(cmdname, "test.battery.start")) {
 
109
                ser_send_pace(upsfd, UPSDELAY, "T\r");
 
110
                return STAT_INSTCMD_HANDLED;
 
111
        }
 
112
 
 
113
        upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname);
 
114
        return STAT_INSTCMD_UNKNOWN;
 
115
}
 
116
 
 
117
 
 
118
static int get_ident(char *buf, size_t bufsize)
 
119
{
 
120
        int     i, ret;
 
121
 
 
122
        for (i = 0; i < MAXTRIES; i++) {
 
123
                ser_send_pace(upsfd, UPSDELAY, "\rP4\r");
 
124
 
 
125
                ret = ser_get_line(upsfd, buf, bufsize, ENDCHAR, "",
 
126
                        SER_WAIT_SEC, SER_WAIT_USEC);
 
127
 
 
128
                if (ret > 0)
 
129
                        upsdebugx(2, "get_ident: got [%s]", buf);
 
130
 
 
131
                /* buf must start with # and be in the range [25-27] */
 
132
                if ((ret > 0) && (buf[0] == '#') && (strlen(buf) >= 25) &&
 
133
                        (strlen(buf) <= 50))
 
134
                        return 1;
 
135
 
 
136
                sleep(1);
 
137
        }
 
138
 
 
139
        upslogx(LOG_INFO, "Giving up on hardware detection after %d tries",
 
140
                MAXTRIES);
 
141
 
 
142
        return 0;
 
143
}
 
144
 
 
145
static int scan_poll_values(char *buf)
 
146
{
 
147
        char values[20][200], *pos;
 
148
        int i = 0, battremain, length, rseconds;
 
149
        double rminutes;
 
150
 
 
151
/*      These are used to hold status of UPS.
 
152
 *      val1 = online/onbattery status
 
153
 */
 
154
        char temp1=values[6][0];
 
155
        char *tmp1=&temp1;
 
156
 
 
157
        while ((pollstatusmap[i].end != 0))
 
158
        {
 
159
                pos = &buf[pollstatusmap[i].begin];
 
160
                length = pollstatusmap[i].end + 1 - pollstatusmap[i].begin;
 
161
                strncpy(values[i],pos,length);
 
162
                i++;
 
163
        }
 
164
 
 
165
        if ((*tmp1 & CPS_STAT_OL) && !(*tmp1 & CPS_STAT_OB)) 
 
166
                status_set("OL");
 
167
 
 
168
        if (*tmp1 & CPS_STAT_OB) 
 
169
                status_set("OB");
 
170
 
 
171
        if (*tmp1 & CPS_STAT_CAL)
 
172
                status_set("CAL");
 
173
 
 
174
        if (*tmp1 & CPS_STAT_LB) 
 
175
                status_set("LB");
 
176
 
 
177
        if (*tmp1 == 0)
 
178
                status_set("OFF");
 
179
 
 
180
 
 
181
        pos = values[3];
 
182
        battremain = strtol(dstate_getinfo("ups.power.nominal"),NULL,10) * (strtod(pos,NULL)/100);
 
183
 
 
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 ;
 
189
 
 
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);
 
197
 
 
198
        status_commit();
 
199
        dstate_dataok();
 
200
        return 0;
 
201
}
 
202
 
 
203
static void ups_ident(void)
 
204
{
 
205
        char    buf[256], *ptr, *com, *model, *rating;
 
206
        int     i;
 
207
 
 
208
        if (!get_ident(buf, sizeof(buf)))
 
209
                fatalx("Unable to detect a CyberPower text protocol UPS");
 
210
 
 
211
        model = rating = NULL;
 
212
 
 
213
        ptr = buf;
 
214
 
 
215
        /* Leaving this in place for future */
 
216
        for (i = 0; i < 2; i++) {
 
217
                com = strchr(ptr, ',');
 
218
 
 
219
                if (com)
 
220
                        *com = '\0';
 
221
 
 
222
                switch (i) {
 
223
                        case 0: model = ptr;
 
224
                                break;
 
225
                        case 1: rating = ptr;
 
226
                                break;
 
227
                        default:
 
228
                                break;
 
229
                }
 
230
 
 
231
                if (com)
 
232
                        ptr = com + 1;
 
233
        }
 
234
 
 
235
        if (!model)
 
236
                fatalx("Didn't get a valid ident string");
 
237
 
 
238
        model_set(model, rating);
 
239
}
 
240
 
 
241
static void ups_sync(void)
 
242
{
 
243
        char    buf[256];
 
244
        int     i, ret;
 
245
 
 
246
        for (i = 0; i < MAXTRIES; i++) {
 
247
                ser_send_pace(upsfd, UPSDELAY, "\rP4\r");
 
248
                upsdebugx(3, "ups_sync: send [%s]", "\\rP4\\r");
 
249
 
 
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);
 
253
 
 
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");
 
257
                        return;
 
258
                }
 
259
 
 
260
                usleep(250000);
 
261
        }
 
262
 
 
263
        fatalx("Unable to detect a CyberPower text protocol UPS");
 
264
}
 
265
 
 
266
void upsdrv_initinfo(void)
 
267
{
 
268
        int ret;
 
269
        char temp[256];
 
270
 
 
271
        ups_sync();
 
272
        ups_ident();
 
273
 
 
274
        printf("Detected %s %s on %s\n", dstate_getinfo("ups.mfr"),
 
275
                dstate_getinfo("ups.model"), device_path);
 
276
 
 
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);
 
280
 
 
281
        upsh.instcmd = instcmd;
 
282
 
 
283
        dstate_setinfo("driver.version.internal", "%s", DRV_VERSION);
 
284
        dstate_addcmd("test.battery.start");
 
285
        dstate_addcmd("test.battery.stop");
 
286
}
 
287
 
 
288
static int ups_on_line(void)
 
289
{
 
290
        int     i, ret;
 
291
        char    temp[256];
 
292
 
 
293
        for (i = 0; i < MAXTRIES; i++) {
 
294
                ser_send_pace(upsfd, UPSDELAY, "\rD\r");
 
295
 
 
296
                ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, "",
 
297
                        SER_WAIT_SEC, SER_WAIT_USEC);
 
298
 
 
299
                /* D must return 34 bytes starting with a # */
 
300
                if ((ret > 0) && (temp[0] == '#') && (strlen(temp) == 34)) {
 
301
 
 
302
                        char * pos = &temp[pollstatusmap[POLL_UPSSTATUS].begin];
 
303
 
 
304
                        if ((*pos & CPS_STAT_OL) && !(*pos & CPS_STAT_OB))
 
305
                                return(1);    /* on line */
 
306
 
 
307
                        return 0;       /* on battery */
 
308
                }
 
309
 
 
310
                sleep(1);
 
311
        }
 
312
 
 
313
        upslogx(LOG_ERR, "Status read failed: assuming on battery");
 
314
 
 
315
        return 0;       /* on battery */
 
316
}
 
317
 
 
318
void upsdrv_shutdown(void)
 
319
{
 
320
        int ret;
 
321
        char    buf[256];
 
322
 
 
323
        ups_sync();
 
324
 
 
325
        printf("The UPS will shut down in approximately one minute.\n");
 
326
 
 
327
        if (ups_on_line())
 
328
                printf("The UPS will restart in about one minute.\n");
 
329
        else
 
330
                printf("The UPS will restart when power returns.\n");
 
331
 
 
332
        /* Although this is straight from the bestups.c driver, the UPS
 
333
         * does indeed shutdown correctly. */
 
334
 
 
335
        ser_send_pace(upsfd, UPSDELAY, "S01R0001\r");
 
336
 
 
337
        ret = ser_get_line(upsfd, buf, sizeof(buf), ENDCHAR, "",
 
338
                                           SER_WAIT_SEC, SER_WAIT_USEC);
 
339
 
 
340
        if ((ret < 1) || (buf[0] != '#'))
 
341
                printf ("Warning: got unexpected reply to shutdown command, shutdown may fail\n");
 
342
}
 
343
 
 
344
void upsdrv_updateinfo(void)
 
345
{
 
346
        char    buf[256];
 
347
        int     ret;
 
348
 
 
349
        ret = ser_send_pace(upsfd, UPSDELAY, "\rD\r");
 
350
 
 
351
        if (ret < 1) {
 
352
                ser_comm_fail("ser_send_pace failed");
 
353
                dstate_datastale();
 
354
                return;
 
355
        }
 
356
 
 
357
        /* these things need a long time to respond completely */
 
358
        usleep(200000);
 
359
 
 
360
        ret = ser_get_line(upsfd, buf, sizeof(buf), ENDCHAR, "",
 
361
                SER_WAIT_SEC, SER_WAIT_USEC);
 
362
 
 
363
        if (ret < 1) {
 
364
                ser_comm_fail(NULL);
 
365
                dstate_datastale();
 
366
                return;
 
367
        }
 
368
 
 
369
        if (ret < 34) {
 
370
                if (ret == 2)           /* We need to retry this read right away    */
 
371
                {
 
372
                        ret = ser_get_line(upsfd, buf, sizeof(buf), ENDCHAR, "", SER_WAIT_SEC, SER_WAIT_USEC);
 
373
                        if (ret < 34) {
 
374
                                ser_comm_fail("Poll failed: short read (got %d bytes)", ret);
 
375
                                dstate_datastale();
 
376
                                return;
 
377
                        }
 
378
                } else {
 
379
                        ser_comm_fail("Poll failed: short read (got %d bytes)", ret);
 
380
                        dstate_datastale();
 
381
                        return;
 
382
                }
 
383
        }
 
384
 
 
385
        if (ret > 34) {
 
386
                upslogx(LOG_INFO, "String too long...");
 
387
                ser_comm_fail("Poll failed: response too long (got %d bytes)",
 
388
                        ret);
 
389
                dstate_datastale();
 
390
                return;
 
391
        }
 
392
 
 
393
        if (buf[0] != '#') {
 
394
                ser_comm_fail("Poll failed: invalid start character (got %02x)",
 
395
                        buf[0]);
 
396
                dstate_datastale();
 
397
                return;
 
398
        }
 
399
 
 
400
        ser_comm_good();
 
401
 
 
402
        scan_poll_values(buf);
 
403
 
 
404
        status_init();
 
405
 
 
406
}
 
407
 
 
408
void upsdrv_help(void)
 
409
{
 
410
}
 
411
 
 
412
void upsdrv_makevartable(void)
 
413
{
 
414
}
 
415
 
 
416
void upsdrv_banner(void)
 
417
{
 
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 */
 
421
}
 
422
 
 
423
void upsdrv_initups(void)
 
424
{
 
425
        upsfd = ser_open(device_path);
 
426
        ser_set_speed(upsfd, device_path, B2400);
 
427
 
 
428
}
 
429
 
 
430
void upsdrv_cleanup(void)
 
431
{
 
432
        ser_close(upsfd, device_path);
 
433
}
 
434