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

« back to all changes in this revision

Viewing changes to drivers/bestups.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:
2
2
 
3
3
   Copyright (C) 1999  Russell Kroll <rkroll@exploits.org>
4
4
 
 
5
   ID config option by Jason White <jdwhite@jdwhite.org>
 
6
 
5
7
   This program is free software; you can redistribute it and/or modify
6
8
   it under the terms of the GNU General Public License as published by
7
9
   the Free Software Foundation; either version 2 of the License, or
18
20
*/
19
21
 
20
22
#include "main.h"
 
23
#include "serial.h"
21
24
 
22
25
#define ENDCHAR  13     /* replies end with CR */
23
 
#define UPSDELAY  5
24
 
#define MAXTRIES 10
25
 
 
26
 
#define DRV_VERSION "0.60"
27
 
 
28
 
static  char    *modelname, *va;
 
26
#define MAXTRIES 5
 
27
#define UPSDELAY 50000  /* 50 ms delay required for reliable operation */
 
28
 
 
29
#define SER_WAIT_SEC    3       /* allow 3.0 sec for ser_get calls */
 
30
#define SER_WAIT_USEC   0
 
31
 
 
32
#define DRV_VERSION "1.03"
 
33
 
29
34
static  float   lowvolt = 0, highvolt = 0, voltrange = 0;
30
 
static  int     linenorm = 0, poll_failures = 0;
 
35
static  int     linenorm = 0;
31
36
static  int     inverted_bypass_bit = 0;
32
37
 
33
 
static void setmodel(const char *abbr, const char *va)
 
38
static void model_set(const char *abbr, const char *rating)
34
39
{
35
40
        if (!strcmp(abbr, "FOR")) {
36
41
                dstate_setinfo("ups.mfr", "%s", "Best Power");
37
 
                dstate_setinfo("ups.model", "Fortress %s", va);
 
42
                dstate_setinfo("ups.model", "Fortress %s", rating);
38
43
                return;
39
44
        }
40
45
 
41
46
        if (!strcmp(abbr, "FTC")) {
42
47
                dstate_setinfo("ups.mfr", "%s", "Best Power");
43
 
                dstate_setinfo("ups.model", "Fortress Telecom %s", va);
 
48
                dstate_setinfo("ups.model", "Fortress Telecom %s", rating);
44
49
                return;
45
50
        }
46
51
 
47
52
        if (!strcmp(abbr, "PRO")) {
48
53
                dstate_setinfo("ups.mfr", "%s", "Best Power");
49
 
                dstate_setinfo("ups.model", "Patriot Pro %s", va);
 
54
                dstate_setinfo("ups.model", "Patriot Pro %s", rating);
50
55
                inverted_bypass_bit = 1;
51
56
                return;
52
57
        }
53
58
 
54
59
        if (!strcmp(abbr, "PR2")) {
55
60
                dstate_setinfo("ups.mfr", "%s", "Best Power");
56
 
                dstate_setinfo("ups.model", "Patriot Pro II %s", va);
 
61
                dstate_setinfo("ups.model", "Patriot Pro II %s", rating);
57
62
                inverted_bypass_bit = 1;
58
63
                return;
59
64
        }
60
65
 
61
66
        if (!strcmp(abbr, "325")) {
62
67
                dstate_setinfo("ups.mfr", "%s", "Sola Australia");
63
 
                dstate_setinfo("ups.model", "Sola 325 %s", va);
 
68
                dstate_setinfo("ups.model", "Sola 325 %s", rating);
64
69
                return;
65
70
        }
66
71
 
67
72
        if (!strcmp(abbr, "520")) {
68
73
                dstate_setinfo("ups.mfr", "%s", "Sola Australia");
69
 
                dstate_setinfo("ups.model", "Sola 520 %s", va);
 
74
                dstate_setinfo("ups.model", "Sola 520 %s", rating);
 
75
                return;
 
76
        }
 
77
 
 
78
        if (!strcmp(abbr, "610")) {
 
79
                dstate_setinfo("ups.mfr", "%s", "Best Power");
 
80
                dstate_setinfo("ups.model", "610 %s", rating);
70
81
                return;
71
82
        }
72
83
 
73
84
        if (!strcmp(abbr, "620")) {
74
85
                dstate_setinfo("ups.mfr", "%s", "Sola Australia");
75
 
                dstate_setinfo("ups.model", "Sola 620 %s", va);
 
86
                dstate_setinfo("ups.model", "Sola 620 %s", rating);
76
87
                return;
77
88
        }
78
89
 
79
90
        if (!strcmp(abbr, "AX1")) {
80
91
                dstate_setinfo("ups.mfr", "%s", "Best Power");
81
 
                dstate_setinfo("ups.model", "Axxium Rackmount %s", va);
 
92
                dstate_setinfo("ups.model", "Axxium Rackmount %s", rating);
82
93
                return;
83
94
        }
84
95
 
85
96
        dstate_setinfo("ups.mfr", "%s", "Unknown");
86
 
        dstate_setinfo("ups.model", "Unknown %s (%s)", abbr, va);
 
97
        dstate_setinfo("ups.model", "Unknown %s (%s)", abbr, rating);
87
98
 
88
99
        printf("Unknown model detected - please report this ID: '%s'\n", abbr);
89
100
}
90
101
 
91
 
int instcmd(const char *cmdname, const char *extra)
 
102
static int instcmd(const char *cmdname, const char *extra)
92
103
{
93
104
        if (!strcasecmp(cmdname, "test.battery.stop")) {
94
 
                upssendchar('C');
95
 
                upssendchar('T');
96
 
                upssendchar(13);
97
 
 
 
105
                ser_send_pace(upsfd, UPSDELAY, "CT\r");
98
106
                return STAT_INSTCMD_HANDLED;
99
107
        }
100
108
 
101
109
        if (!strcasecmp(cmdname, "test.battery.start")) {
102
 
                upssendchar('T');
103
 
                upssendchar(13);
104
 
 
 
110
                ser_send_pace(upsfd, UPSDELAY, "T\r");
105
111
                return STAT_INSTCMD_HANDLED;
106
112
        }
107
113
 
109
115
        return STAT_INSTCMD_UNKNOWN;
110
116
}
111
117
 
112
 
void upsdrv_initinfo(void)
113
 
{
114
 
        dstate_setinfo("driver.version.internal", "%s", DRV_VERSION);
115
 
        dstate_addcmd("test.battery.start");
116
 
        dstate_addcmd("test.battery.stop");
117
 
 
118
 
        setmodel(modelname, va);
119
 
        printf("Detected %s %s on %s\n", dstate_getinfo("ups.mfr"), 
120
 
                dstate_getinfo("ups.model"), device_path);
121
 
 
122
 
        upsh.new_instcmd = instcmd;
123
 
 
124
 
        /* paranoia - cancel any shutdown that might already be running */
125
 
        upssendchar('C');
126
 
        upssendchar(13);
127
 
}
128
 
 
129
 
static void setup_serial(void)
130
 
{       
131
 
        struct  termios tio;
132
 
 
133
 
        if (tcgetattr(upsfd, &tio) == -1)
134
 
                fatal("tcgetattr");
135
 
 
136
 
        tio.c_iflag = IXON | IXOFF;
137
 
        tio.c_oflag = 0;
138
 
        tio.c_cflag = (CS8 | CREAD | HUPCL | CLOCAL);
139
 
        tio.c_lflag = 0;
140
 
        tio.c_cc[VMIN] = 1;
141
 
        tio.c_cc[VTIME] = 0; 
142
 
 
143
 
#ifdef HAVE_CFSETISPEED
144
 
        cfsetispeed(&tio, B2400);
145
 
        cfsetospeed(&tio, B2400);
146
 
#else
147
 
#error This system lacks cfsetispeed() and has no other means to set the speed
148
 
#endif
149
 
 
150
 
        if (tcsetattr(upsfd, TCSANOW, &tio) == -1)
151
 
                fatal("tcsetattr");
152
 
}
153
 
 
154
 
static void ups_sync(void)
155
 
{
156
 
        char    buf[256];
157
 
        int     i, ret;
158
 
 
159
 
        printf("Syncing with UPS: ");
160
 
        fflush(stdout);
161
 
 
162
 
        for (i = 0; i < MAXTRIES; i++) {
163
 
                printf(".");
164
 
                fflush(stdout);
165
 
                upssend("%s", "\rQ1\r");
166
 
                printf(".");
167
 
                fflush(stdout);
168
 
                sleep(UPSDELAY);
169
 
                printf(".");
170
 
                fflush(stdout);
171
 
 
172
 
                ret = upsrecv(buf, sizeof(buf), ENDCHAR, "");
173
 
 
174
 
                /* return once we get something that looks usable */
175
 
                if ((ret > 0) && (buf[0] == '(')) {
176
 
                        printf(" done\n");
177
 
                        return;
178
 
                }
179
 
        }
180
 
 
181
 
        fatalx("\nFailed - giving up...");
182
 
}
183
 
 
184
 
static int ups_on_line(void)
185
 
{
186
 
        int     i, ret;
187
 
        char    temp[256], stat[32];
188
 
 
189
 
        printf("Checking line status: ");
190
 
        fflush(stdout);
191
 
 
192
 
        for (i = 0; i < MAXTRIES; i++) {
193
 
                upssend("\rQ1\r");
194
 
                printf(".");
195
 
                fflush(stdout);
196
 
                sleep(UPSDELAY);
197
 
                printf(".");
198
 
                fflush(stdout);
199
 
 
200
 
                ret = upsrecv(temp, sizeof(temp), ENDCHAR, "");
201
 
 
202
 
                /* Q1 must return 46 bytes starting with a ( */
203
 
                if ((ret > 0) && (temp[0] == '(') && (strlen(temp) == 46)) {
204
 
 
205
 
                        sscanf(temp, "%*s %*s %*s %*s %*s %*s %*s %s", stat);
206
 
 
207
 
                        if (stat[0] == '0')
208
 
                                return 1;       /* on line */
209
 
 
210
 
                        return 0;       /* on battery */
211
 
                }
212
 
        }
213
 
 
214
 
        upslogx(LOG_ERR, "Status read failed: assuming on battery");
215
 
 
216
 
        return 0;       /* on battery */
217
 
}
218
 
 
219
 
void upsdrv_shutdown(void)
220
 
{
221
 
        printf("The UPS will shut down in approximately one minute.\n");
222
 
 
223
 
        if (ups_on_line())
224
 
                printf("The UPS will restart in about one minute.\n");
225
 
        else
226
 
                printf("The UPS will restart when power returns.\n");
227
 
 
228
 
        upssend("%s", "S01R0001\r");
229
 
}
230
 
 
231
118
static int get_ident(char *buf, size_t bufsize)
232
119
{
233
120
        int     i, ret;
234
 
 
235
 
        printf("Identifying UPS: ");
236
 
        fflush(stdout);
 
121
        char    *ID;
 
122
 
 
123
        ID = getval("ID");      /* user-supplied override from ups.conf */
 
124
 
 
125
        if (ID) {
 
126
                upsdebugx(2, "NOTE: using user-supplied ID response");
 
127
                snprintf(buf, bufsize, "%s", ID);
 
128
                return 1;
 
129
        }
237
130
 
238
131
        for (i = 0; i < MAXTRIES; i++) {
239
 
                printf(".");
240
 
                fflush(stdout);
241
 
 
242
 
                upssend("%s", "\r\rID\r");
243
 
                printf(".");
244
 
 
245
 
                fflush(stdout);
246
 
                sleep(UPSDELAY);
247
 
                printf(".");
248
 
                fflush(stdout);
249
 
 
250
 
                ret = upsrecv(buf, bufsize, ENDCHAR, "");
 
132
                ser_send_pace(upsfd, UPSDELAY, "\rID\r");
 
133
 
 
134
                ret = ser_get_line(upsfd, buf, bufsize, ENDCHAR, "", 
 
135
                        SER_WAIT_SEC, SER_WAIT_USEC);
 
136
 
 
137
                if (ret > 0)
 
138
                        upsdebugx(2, "get_ident: got [%s]", buf);
251
139
 
252
140
                /* buf must start with ( and be in the range [25-27] */
253
141
                if ((ret > 0) && (buf[0] != '(') && (strlen(buf) >= 25) &&
254
 
                        (strlen(buf) <= 27)) {
255
 
 
256
 
                        printf(" done\n");
 
142
                        (strlen(buf) <= 27))
257
143
                        return 1;
258
 
                }
 
144
 
 
145
                sleep(1);
259
146
        }
260
147
 
261
148
        upslogx(LOG_INFO, "Giving up on hardware detection after %d tries",
266
153
 
267
154
static void ups_ident(void)
268
155
{
269
 
        char    buf[256], *ptr, *com;
 
156
        char    buf[256], *ptr, *com, *model, *rating, *bvs;
270
157
        int     i;
271
158
 
272
159
        if (!get_ident(buf, sizeof(buf)))
273
 
                fatalx("Unable to get initial hardware info string");
 
160
                fatalx("Unable to detect a Best/SOLA or Phoenix protocol UPS");
274
161
 
275
 
        modelname = va = NULL;
 
162
        model = rating = NULL;
276
163
 
277
164
        /* FOR,750,120,120,20.0,27.6 */
278
165
        ptr = buf;
284
171
                        *com = '\0';
285
172
 
286
173
                switch (i) {
287
 
                        case 0: modelname = xstrdup(ptr);
 
174
                        case 0: model = ptr;
288
175
                                break;
289
 
                        case 1: va = xstrdup(ptr);
 
176
                        case 1: rating = ptr;
290
177
                                break;
291
178
                        case 2: linenorm = atoi(ptr);
292
179
                                break;
293
180
                        case 4: lowvolt = atof(ptr);
294
181
                                break;
295
182
                        case 5: highvolt = atof(ptr);
296
 
                                voltrange = highvolt - lowvolt;
297
183
                                break;
298
184
                        default:
299
185
                                break;
302
188
                if (com)
303
189
                        ptr = com + 1;
304
190
        }
305
 
}
306
 
 
307
 
static void pollfail(char *why)
308
 
{
309
 
        poll_failures++;
310
 
 
311
 
        /* ignore the first few since these UPSes tend to drop characters */
312
 
        if (poll_failures == 3)
313
 
                upslogx(LOG_ERR, why);
314
 
 
315
 
        return;
 
191
 
 
192
        if ((!model) || (!rating))
 
193
                fatalx("Didn't get a valid ident string");
 
194
 
 
195
        bvs = getval("nombattvolt");
 
196
 
 
197
        if (bvs) {
 
198
                float   nomvolt;
 
199
 
 
200
                nomvolt = strtod(bvs, (char **) NULL);
 
201
 
 
202
                if (nomvolt > 0)
 
203
                        highvolt = nomvolt;
 
204
        }
 
205
 
 
206
        dstate_setinfo("battery.voltage.nominal", "%2.1f", highvolt);
 
207
 
 
208
        voltrange = highvolt - lowvolt;
 
209
 
 
210
        model_set(model, rating);
 
211
}
 
212
 
 
213
static void ups_sync(void)
 
214
{
 
215
        char    buf[256];
 
216
        int     i, ret;
 
217
 
 
218
        for (i = 0; i < MAXTRIES; i++) {
 
219
                ser_send_pace(upsfd, UPSDELAY, "\rQ1\r");
 
220
 
 
221
                ret = ser_get_line(upsfd, buf, sizeof(buf), ENDCHAR, "", 
 
222
                        SER_WAIT_SEC, SER_WAIT_USEC);
 
223
 
 
224
                /* return once we get something that looks usable */
 
225
                if ((ret > 0) && (buf[0] == '('))
 
226
                        return;
 
227
 
 
228
                usleep(250000);
 
229
        }
 
230
 
 
231
        fatalx("Unable to detect a Best/SOLA or Phoenix protocol UPS");
 
232
}
 
233
 
 
234
void upsdrv_initinfo(void)
 
235
{
 
236
        ups_sync();
 
237
        ups_ident();
 
238
 
 
239
        printf("Detected %s %s on %s\n", dstate_getinfo("ups.mfr"), 
 
240
                dstate_getinfo("ups.model"), device_path);
 
241
 
 
242
        /* paranoia - cancel any shutdown that might already be running */
 
243
        ser_send_pace(upsfd, UPSDELAY, "C\r");
 
244
 
 
245
        upsh.instcmd = instcmd;
 
246
 
 
247
        dstate_setinfo("driver.version.internal", "%s", DRV_VERSION);
 
248
        dstate_addcmd("test.battery.start");
 
249
        dstate_addcmd("test.battery.stop");
 
250
}
 
251
 
 
252
static int ups_on_line(void)
 
253
{
 
254
        int     i, ret;
 
255
        char    temp[256], pstat[32];
 
256
 
 
257
        for (i = 0; i < MAXTRIES; i++) {
 
258
                ser_send_pace(upsfd, UPSDELAY, "\rQ1\r");
 
259
 
 
260
                ret = ser_get_line(upsfd, temp, sizeof(temp), ENDCHAR, "", 
 
261
                        SER_WAIT_SEC, SER_WAIT_USEC);
 
262
 
 
263
                /* Q1 must return 46 bytes starting with a ( */
 
264
                if ((ret > 0) && (temp[0] == '(') && (strlen(temp) == 46)) {
 
265
 
 
266
                        sscanf(temp, "%*s %*s %*s %*s %*s %*s %*s %s", pstat);
 
267
 
 
268
                        if (pstat[0] == '0')
 
269
                                return 1;       /* on line */
 
270
 
 
271
                        return 0;       /* on battery */
 
272
                }
 
273
 
 
274
                sleep(1);
 
275
        }
 
276
 
 
277
        upslogx(LOG_ERR, "Status read failed: assuming on battery");
 
278
 
 
279
        return 0;       /* on battery */
 
280
}       
 
281
 
 
282
void upsdrv_shutdown(void)
 
283
{
 
284
        printf("The UPS will shut down in approximately one minute.\n");
 
285
 
 
286
        if (ups_on_line())
 
287
                printf("The UPS will restart in about one minute.\n");
 
288
        else
 
289
                printf("The UPS will restart when power returns.\n");
 
290
 
 
291
        ser_send_pace(upsfd, UPSDELAY, "S01R0001\r");
316
292
}
317
293
 
318
294
void upsdrv_updateinfo(void)
319
295
{
320
296
        char    involt[16], outvolt[16], loadpct[16], acfreq[16], 
321
 
                battvolt[16], upstemp[16], stat[16], buf[256];
 
297
                battvolt[16], upstemp[16], pstat[16], buf[256];
322
298
        float   bvoltp;
323
299
        int     ret;
324
 
        static  int     pass = 0;
325
 
 
326
 
        /* this UPS needs a huge delay on queries, so chop up the updates */
327
 
 
328
 
        /* half the time, send the Q1, the other half, read it */
329
 
 
330
 
        if (pass == 0) {
331
 
                upssend("%s", "\rQ1\r");
332
 
                pass = 1;
333
 
                return;
334
 
        }
335
 
 
336
 
        pass = 0;
337
 
 
338
 
        ret = upsrecv(buf, sizeof(buf), ENDCHAR, "");
339
 
 
340
 
        if (strlen(buf) < 46) {
341
 
                pollfail("Poll failed: short read from UPS");
342
 
                dstate_datastale();
343
 
                return;
344
 
        }
345
 
 
346
 
        if (strlen(buf) > 46) {
347
 
                pollfail("Poll failed: oversized read from UPS");
 
300
 
 
301
        ret = ser_send_pace(upsfd, UPSDELAY, "\rQ1\r");
 
302
 
 
303
        if (ret < 1) {
 
304
                ser_comm_fail("ser_send_pace failed");
 
305
                dstate_datastale();
 
306
                return;
 
307
        }
 
308
 
 
309
        /* these things need a long time to respond completely */
 
310
        usleep(200000);
 
311
 
 
312
        ret = ser_get_line(upsfd, buf, sizeof(buf), ENDCHAR, "", 
 
313
                SER_WAIT_SEC, SER_WAIT_USEC);
 
314
 
 
315
        if (ret < 1) {
 
316
                ser_comm_fail(NULL);
 
317
                dstate_datastale();
 
318
                return;
 
319
        }
 
320
 
 
321
        if (ret < 46) {
 
322
                ser_comm_fail("Poll failed: short read (got %d bytes)", ret);
 
323
                dstate_datastale();
 
324
                return;
 
325
        }
 
326
 
 
327
        if (ret > 46) {
 
328
                ser_comm_fail("Poll failed: response too long (got %d bytes)",
 
329
                        ret);
348
330
                dstate_datastale();
349
331
                return;
350
332
        }
351
333
 
352
334
        if (buf[0] != '(') {
353
 
                pollfail("Poll failed: invalid start character");
 
335
                ser_comm_fail("Poll failed: invalid start character (got %02x)",
 
336
                        buf[0]);
354
337
                dstate_datastale();
355
338
                return;
356
339
        }
357
340
 
358
 
        /* only say this if it got high enough to log a failure note */
359
 
        if (poll_failures >= 3)
360
 
                upslogx(LOG_NOTICE, "UPS poll succeeded");
361
 
 
362
 
        poll_failures = 0;
 
341
        ser_comm_good();
363
342
 
364
343
        sscanf(buf, "%*c%s %*s %s %s %s %s %s %s", involt, outvolt, 
365
 
                loadpct, acfreq, battvolt, upstemp, stat);
 
344
                loadpct, acfreq, battvolt, upstemp, pstat);
366
345
        
367
346
        bvoltp = ((atof (battvolt) - lowvolt) / voltrange) * 100.0;
368
347
 
379
358
 
380
359
        status_init();
381
360
 
382
 
        if (stat[0] == '0') {
 
361
        if (pstat[0] == '0') {
383
362
                status_set("OL");               /* on line */
384
363
 
385
364
                /* only allow these when OL since they're bogus when OB */
386
365
 
387
 
                if (stat[2] == (inverted_bypass_bit ? '0' : '1')) {
 
366
                if (pstat[2] == (inverted_bypass_bit ? '0' : '1')) {
388
367
                        /* boost or trim in effect */
389
368
                        if (atof(involt) < atof(outvolt))
390
369
                                status_set("BOOST");
397
376
                status_set("OB");               /* on battery */
398
377
        }
399
378
 
400
 
        if (stat[1] == '1')
 
379
        if (pstat[1] == '1')
401
380
                status_set("LB");               /* low battery */
402
381
 
403
382
        status_commit();
410
389
 
411
390
void upsdrv_makevartable(void)
412
391
{
 
392
        addvar(VAR_VALUE, "nombattvolt", "Override nominal battery voltage");
 
393
        addvar(VAR_VALUE, "ID", "Force UPS ID response string");
413
394
}
414
395
 
415
396
void upsdrv_banner(void)
420
401
 
421
402
void upsdrv_initups(void)
422
403
{
423
 
        upssend_delay = 100000;
424
 
        open_serial(device_path, B2400);
425
 
        setup_serial();
426
 
 
427
 
        /* don't let upscommon warn about it since it happens way too often */
428
 
        /* this driver does its own checking with better handling */
429
 
        flag_timeoutfailure = -1;
430
 
 
431
 
        ups_sync();
432
 
        ups_ident();
 
404
        upsfd = ser_open(device_path);
 
405
        ser_set_speed(upsfd, device_path, B2400);
433
406
}
434
407
 
435
408
void upsdrv_cleanup(void)
436
409
{
 
410
        ser_close(upsfd, device_path);
437
411
}