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

« back to all changes in this revision

Viewing changes to drivers/tripplite.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
1
/* tripplite.c - model specific routines for Tripp Lite SmartUPS models
2
 
   (tested with "SMART 700" [on back -- "SmartPro UPS" on front])
 
2
   (tested with:
 
3
   "SMART 700" [on back -- "SmartPro UPS" on front], "SMART700SER")
3
4
 
4
5
   tripplite.c was derived from Russell Kroll's bestups.c by Rik Faith.
5
6
 
6
7
   Copyright (C) 1999  Russell Kroll <rkroll@exploits.org>
7
8
   Copyright (C) 2001  Rickard E. (Rik) Faith <faith@alephnull.com>
 
9
   Copyright (C) 2004  Nicholas J. Kain <nicholas@kain.us>
8
10
 
9
11
   This program is free software; you can redistribute it and/or modify
10
12
   it under the terms of the GNU General Public License as published by
25
27
 
26
28
   A few magic numbers were derived from the GPL'd file
27
29
   opensrc_server/upscmd.cpp, available from Tripp Lite at
28
 
   http://www.tripplite.com/linux/.  The copyright notice that applies
29
 
   to this file is as follows:
30
 
 
31
 
   The PowerAlert server, the PowerAlert client, and all supporting 
32
 
   documents and files, are Copyright (C) 1998, 1999 by Tripp Lite,
33
 
   unless expressly stated therein.
34
 
 
35
 
   This program is free software; you can redistribute it and/or 
36
 
   modify it under the terms of the GNU General Public License as 
37
 
   published by the Free Software Foundation; either version 2 of 
38
 
   the License, or (at your option) any later version.
39
 
 
40
 
   This program is distributed in the hope that it will be useful, 
41
 
   but WITHOUT ANY WARRANTY; without even the implied warranty of 
42
 
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU 
43
 
   General Public License for more details.
44
 
 
45
 
   You should have received a copy of the GNU General Public License 
46
 
   along with this program; if not, write to the Free Software 
47
 
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 
30
   http://www.tripplite.com/linux/.
48
31
*/
49
32
 
50
33
/* REFERENCE 2
55
38
 
56
39
   These pages confirm the information in the Tripp Lite source code
57
40
   referenced above and add more details.
58
 
   
 
41
 
59
42
   The first page tells how to derive the VA rating from w_value and
60
43
   l_value.  It's a confusing explanation because shifts are used to
61
44
   mask out bits.  Here is an example starting with the formula on the
74
57
                    = 700
75
58
*/
76
59
 
77
 
#define DRV_VERSION "0.02"
 
60
/* Known UPS Commands:
 
61
 *
 
62
 * :N%02X -- delay the UPS for provided time (hex seconds)
 
63
 * :H%06X -- reboot the UPS.  UPS will restart after provided time (hex s)
 
64
 * :A     -- begins a self-test
 
65
 * :C     -- fetches result of a self-test
 
66
 * :K1    -- turns on power receptacles
 
67
 * :K0    -- turns off power receptacles
 
68
 * :G     -- unconfirmed: shuts down UPS until power returns
 
69
 * :Q1    -- enable "Remote Reboot"
 
70
 * :Q0    -- disable "Remote Reboot"
 
71
 * :W     -- returns 'W' data
 
72
 * :L     -- returns 'L' data
 
73
 * :V     -- returns 'V' data (firmware revision)
 
74
 * :X     -- returns 'X' data (firmware revision)
 
75
 * :D     -- returns general status data
 
76
 * :B     -- returns battery voltage (hexadecimal decivolts)
 
77
 * :I     -- returns minimum input voltage (hexadecimal hertz)
 
78
 * :M     -- returns maximum input voltage (hexadecimal hertz)
 
79
 * :P     -- returns power rating
 
80
 * :Z     -- unknown
 
81
 * :U     -- unknown
 
82
 * :O     -- unknown
 
83
 * :E     -- unknown
 
84
 * :Y     -- returns mains frequency  (':D' is preferred)
 
85
 * :T     -- returns ups temperature  (':D' is preferred)
 
86
 * :R     -- returns input voltage    (':D' is preferred)
 
87
 * :F     -- returns load percentage  (':D' is preferred)
 
88
 * :S     -- enables remote reboot/remote power on
 
89
 */
 
90
 
 
91
/*  Returned value from ':D' looks like:
 
92
 *
 
93
 *  0123456789abcdef01234
 
94
 *  ABCCDEFFGGGGHHIIIIJJJ
 
95
 *  A    0=LB   1=OK
 
96
 *  B    0=OVER 1=OK
 
97
 *  CC   INFO_UTILITY
 
98
 *  D    0=normal 1=TRIM 2=BOOST 3="EXTRA BOOST"
 
99
 *  E    0=OFF 1=OB 2=OL 3=OB (1 and 3 are the same?)
 
100
 *  FF   f(INFO_UPSTEMP)
 
101
 *  GG   ? INFO_BATTPCT (00 when OB, values don't match table we use)
 
102
 *  HH   ? (always 00)
 
103
 *  II   INFO_LOADPCT
 
104
 *  JJJJ ? (e.g., 5B82 5B82 5982 037B 0082)
 
105
 *  KKK  INFO_ACFREQ * 10
 
106
 */
 
107
 
 
108
#define DRV_VERSION "0.8"
78
109
 
79
110
#include "main.h"
 
111
#include "serial.h"
 
112
#include <math.h>
80
113
#include <ctype.h>
81
114
 
82
 
#define ENDCHAR  '\n'   /* replies end with CR LF -- use LF to end */
83
 
#define IGNCHARS "\r"   /* ignore CR */
84
 
#define UPSDELAY  1
85
 
#define MAXTRIES  3
86
 
 
87
 
static  char    *w_value, *l_value, *v_value, *x_value;
88
 
static  int     poll_failures = 0;
89
 
 
90
 
static int hex2d(char *start, int len)
91
 
{
92
 
    char buf[256];
93
 
 
94
 
    strncpy(buf, start, len);
95
 
    buf[len] = '\0';
96
 
    return strtol(buf, NULL, 16);
97
 
}
98
 
 
99
 
int instcmd(const char *cmdname, const char *extra)
100
 
{
101
 
        char    buf[256];
 
115
#define ENDCHAR '\n'           /* replies end with CR LF -- use LF to end */
 
116
#define IGNCHAR '\r'           /* ignore CR */
 
117
#define MAXTRIES 3
 
118
#define SER_WAIT_SEC  3        /* allow 3.0 sec for ser_get calls */
 
119
#define SER_WAIT_USEC 0
 
120
#define DEFAULT_OFFDELAY   64  /* seconds (max 0xFF) */
 
121
#define DEFAULT_STARTDELAY 60  /* seconds (max 0xFFFFFF) */
 
122
#define DEFAULT_BOOTDELAY  64  /* seconds (max 0xFF) */
 
123
#define MAX_VOLT 13.4          /* Max battery voltage (100%) */
 
124
#define MIN_VOLT 11.0          /* Min battery voltage (10%) */
 
125
 
 
126
/* We calculate battery charge (q) as a function of voltage (V).
 
127
 * It seems that this function probably varies by firmware revision or
 
128
 * UPS model - the Windows monitoring software gives different q for a
 
129
 * given V than the old open source Tripp Lite monitoring software.
 
130
 *
 
131
 * The discharge curve should be the same for any given battery chemistry,
 
132
 * so it should only be necessary to specify the minimum and maximum
 
133
 * voltages likely to be seen in operation.
 
134
 */
 
135
 
 
136
/* Interval notation for Q% = 10% <= [minV, maxV] <= 100%  */
 
137
static float V_interval[2] = {MIN_VOLT, MAX_VOLT};
 
138
 
 
139
/* Time in seconds to delay before shutting down. */
 
140
static unsigned int offdelay = DEFAULT_OFFDELAY;
 
141
static unsigned int startdelay = DEFAULT_STARTDELAY;
 
142
static unsigned int bootdelay = DEFAULT_BOOTDELAY;
 
143
 
 
144
static int hex2d(char *start, unsigned int len)
 
145
{
 
146
        char buf[32];
 
147
        buf[32] = '\0';
 
148
 
 
149
        strncpy(buf, start, (len < (sizeof buf) ? len : (sizeof buf - 1)));
 
150
        return strtol(buf, NULL, 16);
 
151
}
 
152
 
 
153
/* The UPS that I'm using (SMART700SER) has the bizarre characteristic
 
154
 * of innately echoing back commands.  Therefore, we cannot use
 
155
 * ser_get_line and must manually discard our echoed command.
 
156
 *
 
157
 * All UPS commands are challenge-response, so this function makes things
 
158
 * very clean.
 
159
 *
 
160
 * return: # of chars in buf, excluding terminating \0 */
 
161
static int send_cmd(const char *str, char *buf, size_t len)
 
162
{
 
163
        char c;
 
164
        int ret;
 
165
        size_t i = 0;
 
166
 
 
167
        ser_send(upsfd, str);
 
168
 
 
169
        if (!len || !buf)
 
170
                return -1;
 
171
 
 
172
        for (;;) {
 
173
                ret = ser_get_char(upsfd, &c, SER_WAIT_SEC, SER_WAIT_USEC);
 
174
                if (ret == -1)
 
175
                        return -1;
 
176
                if (c == ENDCHAR)
 
177
                        break;
 
178
        }
 
179
        do {
 
180
                ret = ser_get_char(upsfd, &c, SER_WAIT_SEC, SER_WAIT_USEC);
 
181
                if (ret == -1)
 
182
                        return -1;
 
183
 
 
184
                if (c == IGNCHAR || c == ENDCHAR)
 
185
                        continue;
 
186
                buf[i++] = c;
 
187
        } while (c != ENDCHAR && i < len);
 
188
        buf[i] = '\0';
 
189
        ser_flush_in(upsfd, NULL, 0);
 
190
        return i;
 
191
}
 
192
 
 
193
static void ups_sync(void)
 
194
{
 
195
        char buf[256];
 
196
        int tries, ret;
 
197
 
 
198
        for (tries = 0; tries < MAXTRIES; ++tries) {
 
199
                ret = send_cmd(":W\r", buf, sizeof buf);
 
200
                if ((ret > 0) && isdigit((unsigned char)buf[0]))
 
201
                        return;
 
202
        }
 
203
        fatalx("\nFailed to find UPS - giving up...");
 
204
}
 
205
 
 
206
static int do_reboot_now(void)
 
207
{
 
208
        char buf[256], cmd[16];
 
209
 
 
210
        snprintf(cmd, sizeof cmd, ":H%06X\r", startdelay);
 
211
        return send_cmd(cmd, buf, sizeof buf);
 
212
}
 
213
 
 
214
static void do_reboot(void)
 
215
{
 
216
        char buf[256], cmd[16];
 
217
 
 
218
        snprintf(cmd, sizeof cmd, ":N%02X\r", bootdelay);
 
219
        send_cmd(cmd, buf, sizeof buf);
 
220
        do_reboot_now();
 
221
}
 
222
 
 
223
static int soft_shutdown(void)
 
224
{
 
225
        char buf[256], cmd[16];
 
226
 
 
227
        snprintf(cmd, sizeof cmd, ":N%02X\r", offdelay);
 
228
        send_cmd(cmd, buf, sizeof buf);
 
229
        return send_cmd(":G\r", buf, sizeof buf);
 
230
}
 
231
 
 
232
static int hard_shutdown(void)
 
233
{
 
234
        char buf[256], cmd[16];
 
235
 
 
236
        snprintf(cmd, sizeof cmd, ":N%02X\r", offdelay);
 
237
        send_cmd(cmd, buf, sizeof buf);
 
238
        return send_cmd(":K0\r", buf, sizeof buf);
 
239
}
 
240
 
 
241
static int instcmd(const char *cmdname, const char *extra)
 
242
{
 
243
        char buf[256];
102
244
 
103
245
        if (!strcasecmp(cmdname, "test.battery.start")) {
104
 
                upssend("%s", ":A\r"); /* Start self test */
105
 
                sleep(UPSDELAY);
106
 
                upsrecv(buf, sizeof(buf), ENDCHAR, IGNCHARS);
107
 
 
 
246
                send_cmd(":A\r", buf, sizeof buf);
 
247
                return STAT_INSTCMD_HANDLED;
 
248
        }
 
249
        if (!strcasecmp(cmdname, "load.off")) {
 
250
                send_cmd(":K0\r", buf, sizeof buf);
 
251
                return STAT_INSTCMD_HANDLED;
 
252
        }
 
253
        if (!strcasecmp(cmdname, "load.on")) {
 
254
                send_cmd(":K1\r", buf, sizeof buf);
 
255
                return STAT_INSTCMD_HANDLED;
 
256
        }
 
257
        if (!strcasecmp(cmdname, "shutdown.reboot")) {
 
258
                do_reboot_now();
 
259
                return STAT_INSTCMD_HANDLED;
 
260
        }
 
261
        if (!strcasecmp(cmdname, "shutdown.reboot.graceful")) {
 
262
                do_reboot();
 
263
                return STAT_INSTCMD_HANDLED;
 
264
        }
 
265
        if (!strcasecmp(cmdname, "shutdown.return")) {
 
266
                soft_shutdown();
 
267
                return STAT_INSTCMD_HANDLED;
 
268
        }
 
269
        if (!strcasecmp(cmdname, "shutdown.stayoff")) {
 
270
                hard_shutdown();
108
271
                return STAT_INSTCMD_HANDLED;
109
272
        }
110
273
 
112
275
        return STAT_INSTCMD_UNKNOWN;
113
276
}
114
277
 
 
278
static int setvar(const char *varname, const char *val)
 
279
{
 
280
        if (!strcasecmp(varname, "ups.delay.shutdown")) {
 
281
                offdelay = atoi(val);
 
282
                dstate_setinfo("ups.delay.shutdown", val);
 
283
                return STAT_SET_HANDLED;
 
284
        }
 
285
        if (!strcasecmp(varname, "ups.delay.start")) {
 
286
                startdelay = atoi(val);
 
287
                dstate_setinfo("ups.delay.start", val);
 
288
                return STAT_SET_HANDLED;
 
289
        }
 
290
        if (!strcasecmp(varname, "ups.delay.reboot")) {
 
291
                bootdelay = atoi(val);
 
292
                dstate_setinfo("ups.delay.reboot", val);
 
293
                return STAT_SET_HANDLED;
 
294
        }
 
295
        return STAT_SET_UNKNOWN;
 
296
}
 
297
 
115
298
void upsdrv_initinfo(void)
116
299
{
117
 
    char *model;
118
 
    int  va;
119
 
    long w, l;
120
 
 
121
 
    dstate_addcmd("test.battery.start");        /* Turns off automatically */
122
 
    
123
 
    dstate_setinfo("ups.mfr", "%s", "Tripp Lite");
124
 
 
125
 
    w = hex2d(w_value, 2);
126
 
    l = hex2d(l_value, 2);
127
 
    
128
 
    if (w & 0x40) model = "Unison %d";
129
 
    else          model = "Smart %d";
130
 
 
131
 
    if (w & 0x80) {       /* New VA rating formula */
132
 
        va = ((w & 0x3f) * 32 + (l >> 3)) * 5;
133
 
    } else {                    /* Old VA rating formula */
134
 
        va = l / 2;
135
 
    }
136
 
    
137
 
    dstate_setinfo("ups.model", model, va);
138
 
    dstate_setinfo("ups.firmware", "%c%c", 
139
 
                'A'+v_value[0]-'0', 'A'+v_value[1]-'0');
140
 
 
141
 
/* we need more details on what these really are */
142
 
#if 0
143
 
    setinfo(INFO_FIRMREV1,   "%s", v_value);
144
 
    setinfo(INFO_FIRMREV2,   "%s", x_value);
145
 
    setinfo(INFO_REG1,       "%s", w_value);
146
 
    setinfo(INFO_REG2,       "%s", l_value);
147
 
#endif
148
 
    
149
 
    upsh.new_instcmd = instcmd;     /* Set here instead of upsdrv_initups */
150
 
    
151
 
    printf("Detected %s %s on %s\n",
152
 
           dstate_getinfo("ups.mfr"), dstate_getinfo("ups.model"), device_path);
153
 
 
154
 
    dstate_setinfo("driver.version.internal", "%s", DRV_VERSION);
155
 
}
156
 
 
157
 
static void setup_serial(void)
158
 
{       
159
 
    struct      termios tio;
160
 
    
161
 
    if (tcgetattr(upsfd, &tio) == -1) fatal("tcgetattr");
162
 
    
163
 
    tio.c_iflag     = IXON | IXOFF;
164
 
    tio.c_oflag     = 0;
165
 
    tio.c_cflag     = (CS8 | CREAD | HUPCL | CLOCAL);
166
 
    tio.c_lflag     = 0;
167
 
    tio.c_cc[VMIN]  = 1;
168
 
    tio.c_cc[VTIME] = 0; 
169
 
    
170
 
#ifdef HAVE_CFSETISPEED
171
 
    cfsetispeed(&tio, B2400);
172
 
    cfsetospeed(&tio, B2400);
173
 
#else
174
 
#error This system lacks cfsetispeed() and has no other means to set the speed
175
 
#endif
176
 
 
177
 
    if (tcsetattr(upsfd, TCSANOW, &tio) == -1) fatal("tcsetattr");
178
 
}
179
 
 
180
 
void ups_sync(void)
181
 
{
182
 
    char        buf[256];
183
 
    int         tries = 0;
184
 
    int         ret;
185
 
 
186
 
    for (;;) {
187
 
        tries++;
188
 
        if (tries > MAXTRIES) fatalx("\nFailed - giving up...");
189
 
        
190
 
        upssend("%s", ":W\r");
191
 
        sleep(UPSDELAY);
192
 
        ret = upsrecv(buf, sizeof(buf), ENDCHAR, IGNCHARS);
193
 
        ret = upsrecv(buf, sizeof(buf), ENDCHAR, IGNCHARS);
194
 
        
195
 
        if ((ret > 0) && isdigit((unsigned char) buf[0])) break;
196
 
    }
197
 
}
198
 
 
199
 
void upsdrv_shutdown(void)      /* FIXME: Not yet tested */
200
 
{
201
 
    char        buf[256];
202
 
    int         ret;
203
 
    
204
 
    upssend("%s", ":N40\r"); /* Delay 64 seconds */
205
 
    sleep(UPSDELAY);
206
 
    ret = upsrecv(buf, sizeof(buf), ENDCHAR, IGNCHARS);
207
 
    
208
 
    upssend("%s", ":G\r");   /* Inverter off */
209
 
    sleep(UPSDELAY);
210
 
    ret = upsrecv(buf, sizeof(buf), ENDCHAR, IGNCHARS);
211
 
 
212
 
#if 1
213
 
    /* FIXME: This may prevent UPS from powering up when utility comes
214
 
     * back. */
215
 
    upssend("%s", ":K0\r");  /* Relay off */
216
 
    sleep(UPSDELAY);
217
 
    ret = upsrecv(buf, sizeof(buf), ENDCHAR, IGNCHARS);
218
 
#endif
219
 
}
220
 
 
221
 
void ups_ident(void)
222
 
{
223
 
    char        buf[256];
224
 
    int         ret;
225
 
    
226
 
    upssend("%s", ":W\r");
227
 
    sleep(UPSDELAY);
228
 
    ret = upsrecv(buf, sizeof(buf), ENDCHAR, IGNCHARS);
229
 
    ret = upsrecv(buf, sizeof(buf), ENDCHAR, IGNCHARS);
230
 
    if (w_value) free(w_value);
231
 
    w_value = xstrdup(buf);
232
 
 
233
 
    upssend("%s", ":L\r");
234
 
    sleep(UPSDELAY);
235
 
    ret = upsrecv(buf, sizeof(buf), ENDCHAR, IGNCHARS);
236
 
    ret = upsrecv(buf, sizeof(buf), ENDCHAR, IGNCHARS);
237
 
    if (l_value) free(l_value);
238
 
    l_value = xstrdup(buf);
239
 
    
240
 
    upssend("%s", ":V\r");
241
 
    sleep(UPSDELAY);
242
 
    ret = upsrecv(buf, sizeof(buf), ENDCHAR, IGNCHARS);
243
 
    ret = upsrecv(buf, sizeof(buf), ENDCHAR, IGNCHARS);
244
 
    if (v_value) free(v_value);
245
 
    v_value = xstrdup(buf);
246
 
 
247
 
    upssend("%s", ":X\r");
248
 
    sleep(UPSDELAY);
249
 
    ret = upsrecv(buf, sizeof(buf), ENDCHAR, IGNCHARS);
250
 
    ret = upsrecv(buf, sizeof(buf), ENDCHAR, IGNCHARS);
251
 
    if (x_value) free(x_value);
252
 
    x_value = xstrdup(buf);
253
 
}
254
 
 
255
 
void pollfail(char *why)
256
 
{
257
 
    poll_failures++;
258
 
 
259
 
    /* ignore the first few since these UPSes tend to drop characters */
260
 
    if (poll_failures == 3) upslogx(LOG_ERR, why);
261
 
    
262
 
    return;
 
300
        const char *model;
 
301
        char buf[16], w_value[16], l_value[16], v_value[16], x_value[16];
 
302
        int  va;
 
303
        long w, l;
 
304
 
 
305
 
 
306
        /* Detect the UPS or die. */
 
307
        ups_sync();
 
308
 
 
309
        send_cmd(":W\r", w_value, sizeof w_value);
 
310
        send_cmd(":L\r", l_value, sizeof l_value);
 
311
        send_cmd(":V\r", v_value, sizeof v_value);
 
312
        send_cmd(":X\r", x_value, sizeof x_value);
 
313
 
 
314
        dstate_setinfo("ups.mfr", "%s", "Tripp Lite");
 
315
 
 
316
        w = hex2d(w_value, 2);
 
317
        l = hex2d(l_value, 2);
 
318
 
 
319
        model = "Smart %d";
 
320
        if (w & 0x40)
 
321
                model = "Unison %d";
 
322
 
 
323
        va = ((w & 0x3f) * 32 + (l >> 3)) * 5;  /* New formula */
 
324
        if (!(w & 0x80))
 
325
                va = l / 2;   /* Old formula */
 
326
 
 
327
        dstate_setinfo("ups.model", model, va);
 
328
        dstate_setinfo("ups.firmware", "%c%c",
 
329
                        'A'+v_value[0]-'0', 'A'+v_value[1]-'0');
 
330
 
 
331
        snprintf(buf, sizeof buf, "%d", offdelay);
 
332
        dstate_setinfo("ups.delay.shutdown", buf);
 
333
        dstate_setflags("ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING);
 
334
        dstate_setaux("ups.delay.shutdown", 3);
 
335
        snprintf(buf, sizeof buf, "%d", startdelay);
 
336
        dstate_setinfo("ups.delay.start", buf);
 
337
        dstate_setflags("ups.delay.start", ST_FLAG_RW | ST_FLAG_STRING);
 
338
        dstate_setaux("ups.delay.start", 8);
 
339
        snprintf(buf, sizeof buf, "%d", bootdelay);
 
340
        dstate_setinfo("ups.delay.reboot", buf);
 
341
        dstate_setflags("ups.delay.reboot", ST_FLAG_RW | ST_FLAG_STRING);
 
342
        dstate_setaux("ups.delay.reboot", 3);
 
343
 
 
344
        dstate_addcmd("test.battery.start"); /* Turns off automatically */
 
345
        dstate_addcmd("load.off");
 
346
        dstate_addcmd("load.on");
 
347
        dstate_addcmd("shutdown.reboot");
 
348
        dstate_addcmd("shutdown.reboot.graceful");
 
349
        dstate_addcmd("shutdown.return");
 
350
        dstate_addcmd("shutdown.stayoff");
 
351
 
 
352
        upsh.instcmd = instcmd;
 
353
        upsh.setvar = setvar;
 
354
 
 
355
        printf("Detected %s %s on %s\n",
 
356
        dstate_getinfo("ups.mfr"), dstate_getinfo("ups.model"), device_path);
 
357
 
 
358
        dstate_setinfo("driver.version.internal", "%s", DRV_VERSION);
 
359
}
 
360
 
 
361
void upsdrv_shutdown(void)
 
362
{
 
363
        soft_shutdown();
263
364
}
264
365
 
265
366
void upsdrv_updateinfo(void)
266
367
{
267
 
    char    buf[256], temp[32];
268
 
    int ret;
269
 
    int     bp;
270
 
    float   bv;
271
 
    
272
 
    upssend("%s", ":D\r");
273
 
    sleep(UPSDELAY); 
274
 
    ret = upsrecv(buf, sizeof(buf), ENDCHAR, IGNCHARS);
275
 
    ret = upsrecv(buf, sizeof(buf), ENDCHAR, IGNCHARS);
276
 
    
277
 
    if (strlen(buf) < 21) {
278
 
        pollfail("Poll failed: short read from UPS");
279
 
        dstate_datastale();
280
 
        return;
281
 
    }
282
 
    
283
 
    if (strlen(buf) > 21) {
284
 
        pollfail("Poll failed: oversized read from UPS");
285
 
        dstate_datastale();
286
 
        return;
287
 
    }
288
 
    
289
 
    /* only say this if it got high enough to log a failure note */
290
 
    if (poll_failures >= 3)
291
 
        upslogx(LOG_NOTICE, "UPS poll succeeded");
292
 
    
293
 
    poll_failures = 0;
294
 
 
295
 
/* this looks more like a candidate for upsdebugx, not a variable */
296
 
#if 0
297
 
    setinfo(INFO_REG3,    "%s",     buf);
298
 
    /* 0123456789abcdef01234
299
 
       ABCCDEFFGGGGHHIIIIJJJ
300
 
       A    0=LB   1=OK
301
 
       B    0=OVER 1=OK
302
 
       CC   INFO_UTILITY
303
 
       D    0=normal 1=TRIM 2=BOOST 3="EXTRA BOOST"
304
 
       E    0=OFF 1=OB 2=OL 3=OB (1 and 3 are the same?)
305
 
       FF   f(INFO_UPSTEMP)
306
 
       GG   ? INFO_BATTPCT (00 when OB, values don't match table we use)
307
 
       HH   ? (always 00)
308
 
       II   INFO_LOADPCT
309
 
       JJJJ ? (e.g., 5B82 5B82 5982 037B 0082)
310
 
       KKK  INFO_ACFREQ * 10
311
 
    */
312
 
#endif
313
 
    
314
 
    dstate_setinfo("input.voltage", "%03d",   hex2d(buf + 2, 2));
315
 
    dstate_setinfo("ups.temperature", "%03d",  (int)(hex2d(buf + 6, 2)*0.3636 - 21.0));
316
 
    dstate_setinfo("ups.load", "%03d",   hex2d(buf + 12, 2));
317
 
    dstate_setinfo("input.frequency",  "%02.2f", hex2d(buf + 18, 3) / 10.0);
318
 
    
319
 
    status_init();
320
 
    
321
 
    /* Battery Voltage Condition */
322
 
    switch (buf[0]) {
323
 
    case '0': status_set("LB");                   break; /* Low Battery */
324
 
    case '1':                                     break; /* Normal */
325
 
    default:  sprintf(temp, "BAT-%c?", buf[0]);  
326
 
              status_set(temp);
327
 
                                                  break; /* Unknown */
328
 
    }
329
 
    
330
 
    /* Load State */
331
 
    switch (buf[1]) {
332
 
    case '0': status_set("OVER");                 break; /* Overload */
333
 
    case '1':                                     break; /* Normal */
334
 
    default:  sprintf(temp, "LOAD-%c?", buf[1]); 
335
 
              status_set(temp);
336
 
                                                  break; /* Unknown */
337
 
    }
338
 
    
339
 
    /* Tap State */
340
 
    switch (buf[4]) {
341
 
    case '0':                                     break; /* Normal */
342
 
    case '1': status_set("TRIM");                 break; /* Reducing */ 
343
 
    case '2': status_set("BOOST");                break; /* Boost */
344
 
    case '3': status_set("BOOST");                break; /* Extra Boost */
345
 
    default:  sprintf(temp, "TAP-%c?", buf[4]);  
346
 
              status_set(temp);
347
 
                                                  break; /* Unknown */
348
 
    }
349
 
    
350
 
    /* Mode */
351
 
    switch (buf[5]) {
352
 
    case '0': status_set("OFF");                  break; /* Off */
353
 
    case '1': status_set("OB");                   break; /* On Battery */
354
 
    case '2': status_set("OL");                   break; /* On Line */
355
 
    case '3': status_set("OB");                   break; /* On Battery */
356
 
    default: sprintf(temp, "MODE-%c?", buf[5]);
357
 
              status_set(temp);
358
 
                                                  break; /* Unknown */
359
 
    }
360
 
 
361
 
    status_commit();
362
 
    
363
 
    upssend("%s", ":B\r");
364
 
    sleep(UPSDELAY); 
365
 
    ret = upsrecv(buf, sizeof(buf), ENDCHAR, IGNCHARS);
366
 
    ret = upsrecv(buf, sizeof(buf), ENDCHAR, IGNCHARS);
367
 
    bv = hex2d(buf, 2) / 10;
368
 
    if (bv <= 11.0)      bp =  10;
369
 
    else if (bv <= 11.2) bp =  20;
370
 
    else if (bv <= 11.4) bp =  30;
371
 
    else if (bv <= 11.6) bp =  40;
372
 
    else if (bv <= 11.8) bp =  50;
373
 
    else if (bv <= 12.0) bp =  60;
374
 
    else if (bv <= 12.2) bp =  70;
375
 
    else if (bv <= 12.5) bp =  80;
376
 
    else if (bv <= 13.2) bp =  90;
377
 
    else                 bp = 100;
378
 
    
379
 
    dstate_setinfo("battery.voltage", "%.1f", bv);
380
 
    dstate_setinfo("battery.charge",  "%03d",   bp);
381
 
    
382
 
    upssend("%s", ":M\r");
383
 
    sleep(UPSDELAY); 
384
 
    ret = upsrecv(buf, sizeof(buf), ENDCHAR, IGNCHARS);
385
 
    ret = upsrecv(buf, sizeof(buf), ENDCHAR, IGNCHARS);
386
 
    dstate_setinfo("input.voltage.maximum", "%d", hex2d(buf, 2));
387
 
    
388
 
    upssend("%s", ":I\r");
389
 
    sleep(UPSDELAY); 
390
 
    ret = upsrecv(buf, sizeof(buf), ENDCHAR, IGNCHARS);
391
 
    ret = upsrecv(buf, sizeof(buf), ENDCHAR, IGNCHARS);
392
 
    dstate_setinfo("input.voltage.minimum", "%d", hex2d(buf, 2));
393
 
    
394
 
    upssend("%s", ":C\r");
395
 
    sleep(UPSDELAY);
396
 
    ret = upsrecv(buf, sizeof(buf), ENDCHAR, IGNCHARS);
397
 
    ret = upsrecv(buf, sizeof(buf), ENDCHAR, IGNCHARS);
398
 
    switch (atoi(buf)) {
399
 
    case 0:  dstate_setinfo("ups.test.result", "%s", "OK");                    break;
400
 
    case 1:  dstate_setinfo("ups.test.result", "%s", "Battery Bad (Replace)"); break;
401
 
    case 2:  dstate_setinfo("ups.test.result", "%s", "In Progress");           break;
402
 
    case 3:  dstate_setinfo("ups.test.result", "%s", "Bad Inverter");          break;
403
 
    default: dstate_setinfo("ups.test.result", "Unknown (%s)", buf);           break;
404
 
    }
405
 
 
406
 
    dstate_dataok();
 
368
        char buf[256];
 
369
        int bp;
 
370
        float bv;
 
371
 
 
372
        send_cmd(":D\r", buf, sizeof buf);
 
373
 
 
374
        if (strlen(buf) < 21) {
 
375
                ser_comm_fail("Failed to get data: short read from UPS");
 
376
                dstate_datastale();
 
377
                return;
 
378
        }
 
379
 
 
380
        if (strlen(buf) > 21) {
 
381
                ser_comm_fail("Failed to get data: oversized read from UPS");
 
382
                dstate_datastale();
 
383
                return;
 
384
        }
 
385
 
 
386
        dstate_setinfo("input.voltage", "%0d", hex2d(buf + 2, 2));
 
387
        dstate_setinfo("ups.temperature", "%3d",
 
388
                        (int)(hex2d(buf + 6, 2)*0.3636 - 21.0));
 
389
        dstate_setinfo("ups.load", "%3d", hex2d(buf + 12, 2));
 
390
        dstate_setinfo("input.frequency", "%02.2f", hex2d(buf + 18, 3) / 10.0);
 
391
 
 
392
        status_init();
 
393
 
 
394
        /* Battery Voltage Condition */
 
395
        switch (buf[0]) {
 
396
                case '0': /* Low Battery */
 
397
                        status_set("LB");
 
398
                        break;
 
399
                case '1': /* Normal */
 
400
                        break;
 
401
                default: /* Unknown */
 
402
                        upslogx(LOG_ERR, "Unknown battery state: %c", buf[0]);
 
403
                        break;
 
404
        }
 
405
 
 
406
        /* Load State */
 
407
        switch (buf[1]) {
 
408
                case '0': /* Overload */
 
409
                        status_set("OVER");
 
410
                        break;
 
411
                case '1': /* Normal */
 
412
                        break;
 
413
                default: /* Unknown */
 
414
                        upslogx(LOG_ERR, "Unknown load state: %c", buf[1]);
 
415
                        break;
 
416
        }
 
417
 
 
418
        /* Tap State */
 
419
        switch (buf[4]) {
 
420
                case '0': /* Normal */
 
421
                        break;
 
422
                case '1': /* Reducing */
 
423
                        status_set("TRIM");
 
424
                        break;
 
425
                case '2': /* Boost */
 
426
                case '3': /* Extra Boost */
 
427
                        status_set("BOOST");
 
428
                        break;
 
429
                default: /* Unknown */
 
430
                        upslogx(LOG_ERR, "Unknown tap state: %c", buf[4]);
 
431
                        break;
 
432
        }
 
433
 
 
434
        /* Mode */
 
435
        switch (buf[5]) {
 
436
                case '0': /* Off */
 
437
                        status_set("OFF");
 
438
                        break;
 
439
                case '1': /* On Battery */
 
440
                        status_set("OB");
 
441
                        break;
 
442
                case '2': /* On Line */
 
443
                        status_set("OL");
 
444
                        break;
 
445
                case '3': /* On Battery */
 
446
                        status_set("OB");
 
447
                        break;
 
448
                default: /* Unknown */
 
449
                        upslogx(LOG_ERR, "Unknown mode state: %c", buf[4]);
 
450
                        break;
 
451
        }
 
452
 
 
453
        status_commit();
 
454
        send_cmd(":B\r", buf, sizeof buf);
 
455
        bv = (float)hex2d(buf, 2) / 10.0;
 
456
 
 
457
        /* dq ~= sqrt(dV) is a reasonable approximation
 
458
         * Results fit well against the discrete function used in the Tripp Lite
 
459
         * source, but give a continuous result. */
 
460
        if (bv >= V_interval[1])
 
461
                bp = 100;
 
462
        else if (bv <= V_interval[0])
 
463
                bp = 10;
 
464
        else
 
465
                bp = (int)(100*sqrt((bv - V_interval[0])
 
466
                                                        / (V_interval[1] - V_interval[0])));
 
467
 
 
468
        dstate_setinfo("battery.voltage", "%.1f", bv);
 
469
        dstate_setinfo("battery.charge",  "%3d", bp);
 
470
 
 
471
        send_cmd(":M\r", buf, sizeof buf);
 
472
        dstate_setinfo("input.voltage.maximum", "%d", hex2d(buf, 2));
 
473
 
 
474
        send_cmd(":I\r", buf, sizeof buf);
 
475
        dstate_setinfo("input.voltage.minimum", "%d", hex2d(buf, 2));
 
476
 
 
477
        send_cmd(":C\r", buf, sizeof buf);
 
478
        switch (atoi(buf)) {
 
479
                case 0:
 
480
                        dstate_setinfo("ups.test.result", "%s", "OK");
 
481
                        break;
 
482
                case 1:
 
483
                        dstate_setinfo("ups.test.result", "%s", "Battery Bad (Replace)");
 
484
                        break;
 
485
                case 2:
 
486
                        dstate_setinfo("ups.test.result", "%s", "In Progress");
 
487
                        break;
 
488
                case 3:
 
489
                        dstate_setinfo("ups.test.result", "%s", "Bad Inverter");
 
490
                        break;
 
491
                default:
 
492
                        dstate_setinfo("ups.test.result", "Unknown (%s)", buf);
 
493
                        break;
 
494
        }
 
495
 
 
496
        dstate_dataok();
 
497
        ser_comm_good();
407
498
}
408
499
 
409
500
void upsdrv_help(void)
412
503
 
413
504
void upsdrv_makevartable(void)
414
505
{
 
506
        char msg[256];
 
507
 
 
508
        snprintf(msg, sizeof msg, "Set shutdown delay, in seconds (default=%d).",
 
509
                DEFAULT_OFFDELAY);
 
510
        addvar(VAR_VALUE, "offdelay", msg);
 
511
        snprintf(msg, sizeof msg, "Set start delay, in seconds (default=%d).",
 
512
                DEFAULT_STARTDELAY);
 
513
        addvar(VAR_VALUE, "startdelay", msg);
 
514
        snprintf(msg, sizeof msg, "Set reboot delay, in seconds (default=%d).",
 
515
                DEFAULT_BOOTDELAY);
 
516
        addvar(VAR_VALUE, "rebootdelay", msg);
415
517
}
416
518
 
417
519
void upsdrv_banner(void)
418
520
{
419
 
    printf("Network UPS Tools - Tripp-Lite SmartUPS driver %s (%s)\n",
420
 
           DRV_VERSION, UPS_VERSION);
 
521
        printf("Network UPS Tools - Tripp-Lite SmartUPS driver %s (%s)\n",
 
522
                        DRV_VERSION, UPS_VERSION);
421
523
}
422
524
 
423
525
void upsdrv_initups(void)
424
526
{
425
 
    open_serial(device_path, B2400);
426
 
    setup_serial();
427
 
    
428
 
    /* don't let upscommon warn about it since it happens way too often */
429
 
    /* this driver does its own checking with better handling */
430
 
    flag_timeoutfailure = -1;
431
 
    
432
 
    ups_sync();
433
 
    ups_ident();
 
527
        upsfd = ser_open(device_path);
 
528
        ser_set_speed(upsfd, device_path, B2400);
 
529
 
 
530
        if (getval("offdelay"))
 
531
                offdelay = atoi(getval("offdelay"));
 
532
        if (getval("startdelay"))
 
533
                startdelay = atoi(getval("startdelay"));
 
534
        if (getval("rebootdelay"))
 
535
                bootdelay = atoi(getval("rebootdelay"));
434
536
}
435
537
 
436
538
void upsdrv_cleanup(void)
437
539
{
 
540
        ser_close(upsfd, device_path);
438
541
}
 
542