1 /* liebert.c - support for Liebert UPS models via MultiLink cable.
2
3 Copyright (C) 2002 Russell Kroll <rkroll@exploits.org>
4
5 Based on old-style multilink.c driver:
6
7 Copyright (C) 2001 Rick Lyons <rick@powerup.com.au>
8
9 This program is free software; you can redistribute it and/or modify
10 it under the terms of the GNU General Public License as published by
11 the Free Software Foundation; either version 2 of the License, or
12 (at your option) any later version.
13
14 This program is distributed in the hope that it will be useful,
15 but WITHOUT ANY WARRANTY; without even the implied warranty of
16 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 GNU General Public License for more details.
18
19 You should have received a copy of the GNU General Public License
20 along with this program; if not, write to the Free Software
21 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24 #include "config.h"
25 #include "main.h"
26 #include "serial.h"
27 #include "attribute.h"
28
29 #define DRIVER_NAME "Liebert MultiLink UPS driver"
30 #define DRIVER_VERSION "1.02"
31
32 /* driver description structure */
33 upsdrv_info_t upsdrv_info = {
34 DRIVER_NAME,
35 DRIVER_VERSION,
36 "Russell Kroll <rkroll@exploits.org>\n" \
37 "Rick Lyons <rick@powerup.com.au>",
38 DRV_EXPERIMENTAL,
39 { NULL }
40 };
41
42 #define ML_ONBATTERY 0x55
43
44 void upsdrv_shutdown(void)
45 __attribute__((noreturn));
46
upsdrv_shutdown(void)47 void upsdrv_shutdown(void)
48 {
49 /* XXX: replace with a proper shutdown function (raise DTR) */
50
51 /* worse yet: stock cables don't support shutdown at all */
52
53 fatalx(EXIT_FAILURE, "shutdown not supported");
54 }
55
upsdrv_initinfo(void)56 void upsdrv_initinfo(void)
57 {
58 char *tmp;
59
60 tmp = getval("mfr");
61
62 if (!tmp)
63 dstate_setinfo("ups.mfr", "Liebert");
64 else
65 dstate_setinfo("ups.mfr", "%s", tmp);
66
67 tmp = getval("model");
68
69 if (!tmp)
70 dstate_setinfo("ups.model", "MultiLink");
71 else
72 dstate_setinfo("ups.model", "%s", tmp);
73 }
74
75 /* require this many OBs or LBs before actually setting it */
76 #define DEBOUNCE 3
77
78 /* normal idle loop - keep up with the current state of the UPS */
upsdrv_updateinfo(void)79 void upsdrv_updateinfo(void)
80 {
81 unsigned char c;
82 unsigned int ob, lb;
83 static unsigned int ob_state = 0, ob_last = 0, ob_ctr = 0;
84 static unsigned int lb_state = 0, lb_last = 0, lb_ctr = 0;
85
86 ob = lb = 0;
87
88 /* the UPS connects RX to TX when on battery, so test for loopback */
89
90 ser_flush_in(upsfd, "", 0);
91
92 c = ML_ONBATTERY;
93 ser_send_char(upsfd, c);
94 if (ser_get_char(upsfd, &c, 1, 0) == 1) {
95 while (ser_get_char(upsfd, &c, 1, 0) == 1)
96 continue;
97 if (c == ML_ONBATTERY)
98 ob = 1;
99 }
100
101 if (ser_get_dcd(upsfd))
102 lb = 1;
103
104 /* state machine below to ensure status changes are debounced */
105
106 /* OB/OL state change: reset counter */
107 if (ob_last != ob)
108 ob_ctr = 0;
109 else
110 ob_ctr++;
111
112 upsdebugx(2, "OB: state %d last %d now %d ctr %d",
113 ob_state, ob_last, ob, ob_ctr);
114
115 if (ob_ctr >= DEBOUNCE) {
116
117 if (ob != ob_state) {
118
119 upsdebugx(2, "OB: toggling state");
120
121 if (ob_state == 0)
122 ob_state = 1;
123 else
124 ob_state = 0;
125 }
126 }
127
128 ob_last = ob;
129
130 /* now do it again for LB */
131
132 /* state change: reset counter */
133 if (lb_last != lb)
134 lb_ctr = 0;
135 else
136 lb_ctr++;
137
138 upsdebugx(2, "LB: state %d last %d now %d ctr %d",
139 lb_state, lb_last, lb, lb_ctr);
140
141 if (lb_ctr >= DEBOUNCE) {
142
143 if (lb != lb_state) {
144
145 upsdebugx(2, "LB: toggling state");
146
147 if (lb_state == 0)
148 lb_state = 1;
149 else
150 lb_state = 0;
151 }
152 }
153
154 lb_last = lb;
155
156 status_init();
157
158 if (ob_state == 1)
159 status_set("OB"); /* on battery */
160 else
161 status_set("OL"); /* on line */
162
163 if (lb_state == 1)
164 status_set("LB"); /* low battery */
165
166 status_commit();
167 dstate_dataok();
168 }
169
upsdrv_makevartable(void)170 void upsdrv_makevartable(void)
171 {
172 addvar(VAR_VALUE, "mfr", "Override manufacturer name");
173 addvar(VAR_VALUE, "model", "Override model name");
174 }
175
upsdrv_help(void)176 void upsdrv_help(void)
177 {
178 }
179
upsdrv_initups(void)180 void upsdrv_initups(void)
181 {
182 upsfd = ser_open(device_path);
183
184 /* Speed should not matter (see comments in upsdrv_updateinfo),
185 * but set it relatively low in case there are problems with higher
186 * speeds. */
187 ser_set_speed(upsfd, device_path, B9600);
188
189 /* raise RTS */
190 ser_set_rts(upsfd, 1);
191 }
192
upsdrv_cleanup(void)193 void upsdrv_cleanup(void)
194 {
195 ser_close(upsfd, device_path);
196 }
197