24
24
#include <asm/types.h>
25
25
#include <sys/ioctl.h>
27
#ifndef HID_MAX_USAGES
28
#define HID_MAX_USAGES 1024 /* horrible workaround hack */
26
31
#include <linux/hiddev.h>
29
34
#include <signal.h>
30
35
#include <sys/select.h>
34
#define DRV_VERSION "0.01"
40
#define DRV_VERSION "0.02"
36
42
#define NUM_EVTS 64
38
46
/* Response to I identification queries is in the following format:
40
48
* 012345678901234567890123456789012345678
41
49
* #Energizer ER-HMOF600 A0 ^M
42
50
* #Energizer ER-OF800 A0 ^M
44
#define MANUFR (buf+1)
45
#define MDLNUM (buf+17)
46
#define HWVERS (buf+28)
52
#define MANUFR (buf+1)
53
#define MDLNUM (buf+17)
54
#define HWVERS (buf+28)
48
56
/* Response to Q1 queries is in the following format:
50
58
* 01234567890123456789012345678901234567890123456
51
59
* (118.6 118.6 118.6 020 60.0 13.8 32.0 00001000^M
53
#define INVOLT (buf+1)
54
#define OUTVOLT (buf+13)
55
#define LOADPCT (buf+19)
56
#define ACFREQ (buf+23)
57
#define BATVOLT (buf+28)
58
#define UPSTEMP (buf+33)
59
#define STATUS (buf+38)
61
#define INVOLT (buf+1)
62
#define OUTVOLT (buf+13)
63
#define LOADPCT (buf+19)
64
#define ACFREQ (buf+23)
65
#define BATVOLT (buf+28)
66
#define UPSTEMP (buf+33)
67
#define STATUS (buf+38)
61
#define OBFLAG (STATUS[0]=='1'||STATUS[5]=='1')
62
#define LBFLAG (STATUS[1]=='1')
63
#define BYPASS (STATUS[2]=='1')
64
#define BEEPER (STATUS[7]=='1')
69
#define OBFLAG (STATUS[0]=='1'||STATUS[5]=='1')
70
#define LBFLAG (STATUS[1]=='1')
71
#define BYPASS (STATUS[2]=='1')
72
#define BEEPER (STATUS[7]=='1')
66
74
/* A calibration test of the ER-HMOF600 shows its maximum voltage at
67
* 14.0V during charging, falling down to 13.6 if the UPS is turned off
68
* for three minutes (but connected to line power) and to 12.8V
75
* 14.0V during charging, falling to 13.6 when the UPS is turned off
76
* for three minutes (while still connected to line power) and to 12.8V
69
77
* immediately after starting a test with a 60W load. The 'low battery'
70
* warning came at 11.0V, and a discharge time to LB with a 60W light
71
* bulb took 35 minutes. The UPS ran another 5 minutes on LB before
78
* warning came on at 11.0V. The discharge time to LB with a 60W light
79
* bulb was 35 minutes. The UPS ran another 5 minutes on LB before
72
80
* shutting down, at which time the battery voltage was 10.1V. When
73
81
* charging resumed at the moment the LB warning came on, the battery
74
82
* started at 12.0V.
104
112
void sendstring(int fd, char *psz)
106
struct hiddev_report_info rinfo;
107
struct hiddev_usage_ref uref;
111
uref.usage_index = 0;
116
c = (*psz) ? *psz :0x0D;
117
for (j = 0; j < 8; j++)
119
uref.report_type = HID_REPORT_TYPE_OUTPUT;
121
uref.field_index = 0;
122
uref.usage_code = 0x90001 + uref.usage_index;
123
uref.value = (c & 1);
124
if (ioctl(fd, HIDIOCSUSAGE, &uref) < 0)
125
fatalx("Error (SUSAGE) while talking to the UPS");
129
if (*psz == 0 || ++i == 8)
132
while (uref.usage_index++ < 63)
134
uref.report_type = HID_REPORT_TYPE_OUTPUT;
136
uref.field_index = 0;
137
uref.usage_code = 0x90001 + uref.usage_index;
139
if (ioctl(fd, HIDIOCSUSAGE, &uref) < 0)
140
fatalx("Error (SUSAGE) while talking to the UPS");
142
uref.usage_index = 0;
144
rinfo.report_type = HID_REPORT_TYPE_OUTPUT;
146
rinfo.num_fields = 1;
147
if (ioctl(fd, HIDIOCSREPORT, &rinfo) < 0)
148
fatalx("Error (SREPORT) while talking to the UPS");
114
struct hiddev_report_info rinfo;
115
struct hiddev_usage_ref uref;
119
uref.usage_index = 0;
124
c = (*psz) ? *psz :0x0D;
125
for (j = 0; j < 8; j++)
127
uref.report_type = HID_REPORT_TYPE_OUTPUT;
129
uref.field_index = 0;
130
uref.usage_code = 0x90001 + uref.usage_index;
131
uref.value = (c & 1);
132
if (ioctl(fd, HIDIOCSUSAGE, &uref) < 0)
133
fatalx("Error (SUSAGE) while talking to the UPS");
137
if (*psz == 0 || ++i == 8)
140
while (uref.usage_index++ < 63)
142
uref.report_type = HID_REPORT_TYPE_OUTPUT;
144
uref.field_index = 0;
145
uref.usage_code = 0x90001 + uref.usage_index;
147
if (ioctl(fd, HIDIOCSUSAGE, &uref) < 0)
148
fatalx("Error (SUSAGE) while talking to the UPS");
150
uref.usage_index = 0;
152
rinfo.report_type = HID_REPORT_TYPE_OUTPUT;
154
rinfo.num_fields = 1;
155
if (ioctl(fd, HIDIOCSREPORT, &rinfo) < 0)
156
fatalx("Error (SREPORT) while talking to the UPS");
162
* Helper function to "fake" the first eight bytes of the UPS response. For
163
* reasons that I don't understand, on 2.6.x kernels the first eight characters
164
* never appear (i.e., the event interface starts with a set of blank
165
* characters, then we receive events that show changed bits... for the
166
* SECOND set of 8 characters. This is admittedly a disgusting hack, but then
167
* again, this serial-to-USB interface is such a hack anyway, it's a miracle it
168
* works! In any case, the point is that this way, we can make the UPS work,
169
* which is the whole point of this exercise.
171
int fake_hid_ev_bits(int r, char *buf, int size)
175
memmove(buf + 8, buf, size - 8);
176
strncpy(buf, "#Energiz", 8);
179
else if ((r == 38 || r == 39) && isdigit(buf[0]))
181
memmove(buf + 8, buf, size - 8);
182
strncpy(buf, "(117.0 1", 8);
168
203
int hidcmd(unsigned char *pCmd, unsigned char *pRsp, int l)
170
int fd, rd, i, j, k, hid;
173
struct hiddev_event ev[NUM_EVTS];
174
struct hiddev_usage_ref uref;
175
unsigned char data[8];
177
if ((fd = open(device_path, O_RDWR)) < 0)
178
fatalx("Cannot communicate with UPS at %s", device_path);
183
memset(data, 0, sizeof(data));
184
for (i = 0; i < 64; i++)
186
uref.report_type = HID_REPORT_TYPE_INPUT;
188
uref.field_index = 0;
189
uref.usage_index = i;
190
ioctl(fd, HIDIOCGUCODE, &uref);
191
ioctl(fd, HIDIOCGUSAGE, &uref);
193
if (uref.value) data[i >> 3] |= 1 << (i & 7);
196
sendstring(fd, pCmd);
207
if (select(fd+1, &rdfs, 0, 0, &tv) <= 0) break;
208
rd = read(fd, ev, sizeof(ev));
209
if (rd < (int) sizeof(ev[0]))
210
fatalx("Communication failure with UPS");
212
for (i = 0; i < rd / sizeof(ev[0]); i++)
214
if (hid >= 0 && ev[i].hid <= hid)
216
for (j = 0; j < 8; j++)
220
if (k > 0 || data[j] != 0xFF) pRsp[k++] = data[j];
225
j = (ev[i].hid - 1) & 0x3F;
226
if (ev[i].value) data[j >> 3] |= 1 << (j & 7);
227
else data[j >> 3] &= ~(1 << (j & 7));
231
if (hid >= 0) for (j = 0; j < ((hid - 1) & 0x3F) >> 3; j++)
235
if (k > 0 || data[j] != 0xFF) pRsp[k++] = data[j];
206
int fd, rd, j, k, hid;
209
struct hiddev_event ev[NUM_EVTS];
210
struct hiddev_usage_ref uref;
211
unsigned char data[8];
213
if ((fd = open(device_path, O_RDWR)) < 0)
214
fatalx("Cannot communicate with UPS at %s", device_path);
219
memset(data, 0, sizeof(data));
220
for (i = 0; i < 64; i++)
222
uref.report_type = HID_REPORT_TYPE_INPUT;
224
uref.field_index = 0;
225
uref.usage_index = i;
226
ioctl(fd, HIDIOCGUCODE, &uref);
227
ioctl(fd, HIDIOCGUSAGE, &uref);
229
if (uref.value) data[i >> 3] |= 1 << (i & 7);
232
sendstring(fd, pCmd);
243
if (select(fd+1, &rdfs, 0, 0, &tv) <= 0) break;
244
rd = read(fd, ev, sizeof(ev));
245
if (rd < (int) sizeof(ev[0]))
246
fatalx("Communication failure with UPS");
248
for (i = 0; i < rd / sizeof(ev[0]); i++)
250
if (hid >= 0 && (int) ev[i].hid <= hid)
252
for (j = 0; j < 8; j++)
256
if (k > 0 || data[j] != 0xFF) pRsp[k++] = data[j];
261
j = (ev[i].hid - 1) & 0x3F;
262
if (ev[i].value) data[j >> 3] |= 1 << (j & 7);
263
else data[j >> 3] &= ~(1 << (j & 7));
267
if (hid >= 0) for (j = 0; j < ((hid - 1) & 0x3F) >> 3; j++)
271
if (k > 0 || data[j] != 0xFF) pRsp[k++] = data[j];
278
return pRsp ? fake_hid_ev_bits(k, pRsp, l) : k;
246
282
int instcmd(const char *cmdname, const char *extra)
248
if (!strcasecmp(cmdname, "test.battery.start"))
251
upslogx(LOG_NOTICE, "UPS test start");
252
return STAT_INSTCMD_HANDLED;
255
if (!strcasecmp(cmdname, "test.battery.stop"))
258
upslogx(LOG_NOTICE, "UPS test stop");
259
return STAT_INSTCMD_HANDLED;
262
if (!strcasecmp(cmdname, "beeper.on") ||
263
!strcasecmp(cmdname, "beeper.off"))
268
/* Find out beeper status and send appropriate command. */
270
r = hidcmd("Q1", buf, sizeof(buf));
271
if (r < 46 || r > 47 || buf[0] != '(')
272
r = hidcmd("Q1", buf, sizeof(buf));
273
if (r >= 46 && r <= 47 && buf[0] == '(')
275
if (OBFLAG && !LBFLAG) /* Otherwise there's not much we can do */
277
if ((BEEPER && !strcasecmp(cmdname, "beeper.off")) ||
278
(!BEEPER && !strcasecmp(cmdname, "beeper.on")))
280
return STAT_INSTCMD_HANDLED;
283
return STAT_INSTCMD_HANDLED; /* FUTURE: failure */
286
upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname);
287
return STAT_INSTCMD_UNKNOWN;
284
if (!strcasecmp(cmdname, "test.battery.start"))
287
upslogx(LOG_NOTICE, "UPS test start");
288
return STAT_INSTCMD_HANDLED;
291
if (!strcasecmp(cmdname, "test.battery.stop"))
294
upslogx(LOG_NOTICE, "UPS test stop");
295
return STAT_INSTCMD_HANDLED;
298
if (!strcasecmp(cmdname, "beeper.on") ||
299
!strcasecmp(cmdname, "beeper.off"))
305
/* Find out beeper status and send appropriate command. */
307
r = hidcmd("Q1", buf, sizeof(buf));
308
while (c++ < RETRIES && (r < 46 || r > 47 || buf[0] != '('))
309
r = hidcmd("Q1", buf, sizeof(buf));
310
if (r >= 46 && r <= 47 && buf[0] == '(')
312
if (OBFLAG && !LBFLAG) /* Otherwise there's not much we can do */
314
if ((BEEPER && !strcasecmp(cmdname, "beeper.off")) ||
315
(!BEEPER && !strcasecmp(cmdname, "beeper.on")))
317
return STAT_INSTCMD_HANDLED;
320
return STAT_INSTCMD_HANDLED; /* FUTURE: failure */
323
upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname);
324
return STAT_INSTCMD_UNKNOWN;
294
331
void upsdrv_initinfo(void)
299
/* Yes, sometimes it took as many as 6-7 tries after the UPS has just
300
* been turned on or after a communications problem.
302
for (i = 0; i < 10; i++)
304
r = hidcmd("I", buf, sizeof(buf));
305
if (r == 39 && buf[0] == '#') break;
308
if (r != 39 || buf[0] != '#') fatalx("No Energizer UPS detected");
309
buf[16]=buf[27]=buf[38] = '\0';
314
dstate_setinfo("driver.version.internal", "%s", DRV_VERSION);
315
dstate_setinfo("ups.mfr", MANUFR);
316
dstate_setinfo("ups.model", MDLNUM);
318
dstate_setinfo("input.transfer.low", "%d", LOXFER);
319
dstate_setinfo("input.transfer.high", "%d", HIXFER);
323
/* now add instant command support info */
324
dstate_addcmd("test.battery.start");
325
dstate_addcmd("test.battery.stop");
326
dstate_addcmd("beeper.on");
327
dstate_addcmd("beeper.off");
328
upsh.new_instcmd = instcmd;
336
/* Yes, sometimes it took as many as 6-7 tries after the UPS has just
337
* been turned on or after a communications problem.
339
for (i = 0; i < 10; i++)
341
r = hidcmd("I", buf, sizeof(buf));
342
if (r == 39 && buf[0] == '#') break;
345
if (r != 39 || buf[0] != '#') fatalx("No Energizer UPS detected");
346
buf[16]=buf[27]=buf[38] = '\0';
351
dstate_setinfo("driver.version.internal", "%s", DRV_VERSION);
352
dstate_setinfo("ups.mfr", "%s", MANUFR);
353
dstate_setinfo("ups.model", "%s", MDLNUM);
355
dstate_setinfo("input.transfer.low", "%d", LOXFER);
356
dstate_setinfo("input.transfer.high", "%d", HIXFER);
360
/* now add instant command support info */
361
dstate_addcmd("test.battery.start");
362
dstate_addcmd("test.battery.stop");
363
dstate_addcmd("beeper.on");
364
dstate_addcmd("beeper.off");
365
upsh.instcmd = instcmd;
335
372
void upsdrv_updateinfo(void)
339
static int f; /* To prevent excessive logging */
343
r = hidcmd("Q1", buf, sizeof(buf));
344
if (r < 46 || r > 47)
347
upslogx(LOG_ERR, "Invalid response length from UPS [%s]", buf);
354
upslogx(LOG_ERR, "Invalid response data from UPS [%s]", buf);
360
buf[6]=buf[12]=buf[18]=buf[22]=buf[27]=buf[32]=buf[37]=buf[46] = '\0';
362
dstate_setinfo("input.voltage", "%s", INVOLT);
363
dstate_setinfo("output.voltage", "%s", OUTVOLT);
364
dstate_setinfo("battery.voltage", "%s", BATVOLT);
366
v = ((atof(BATVOLT) - LOWVOLT) / VOLTRNG) * 100.0;
367
if (v > 100.0) v = 100.0;
368
if (v < 0.0) v = 0.0;
369
dstate_setinfo("battery.charge", "%02.1f", v);
372
if (OBFLAG) status_set("OB"); /* on battery */
375
status_set("OL"); /* on line */
376
/* only allow these when OL since they're bogus when OB */
377
if (BYPASS) /* boost or trim in effect */
379
if (atoi(INVOLT) < LONORM) status_set("BOOST");
380
else if (atoi(INVOLT) > HINORM) status_set("TRIM");
383
if (LBFLAG) status_set("LB"); /* low battery */
387
dstate_setinfo("ups.temperature", "%s", UPSTEMP);
388
dstate_setinfo("input.frequency", "%s", ACFREQ);
389
dstate_setinfo("ups.load", "%s", LOADPCT);
376
static int f; /* To prevent excessive logging */
380
r = hidcmd("Q1", buf, sizeof(buf));
381
if (r < 46 || r > 47)
384
upslogx(LOG_ERR, "Invalid response length from UPS [%s]", buf);
392
upslogx(LOG_ERR, "Invalid response data from UPS [%s]", buf);
399
buf[6]=buf[12]=buf[18]=buf[22]=buf[27]=buf[32]=buf[37]=buf[46] = '\0';
401
dstate_setinfo("input.voltage", "%s", INVOLT);
402
dstate_setinfo("output.voltage", "%s", OUTVOLT);
403
dstate_setinfo("battery.voltage", "%s", BATVOLT);
405
v = ((atof(BATVOLT) - LOWVOLT) / VOLTRNG) * 100.0;
406
if (v > 100.0) v = 100.0;
407
if (v < 0.0) v = 0.0;
408
dstate_setinfo("battery.charge", "%02.1f", v);
411
if (OBFLAG) status_set("OB"); /* on battery */
414
status_set("OL"); /* on line */
415
/* only allow these when OL since they're bogus when OB */
416
if (BYPASS) /* boost or trim in effect */
418
if (atoi(INVOLT) < LONORM) status_set("BOOST");
419
else if (atoi(INVOLT) > HINORM) status_set("TRIM");
422
if (LBFLAG) status_set("LB"); /* low battery */
426
dstate_setinfo("ups.temperature", "%s", UPSTEMP);
427
dstate_setinfo("input.frequency", "%s", ACFREQ);
428
dstate_setinfo("ups.load", "%s", LOADPCT);
393
432
void upsdrv_shutdown(void)
399
/* Basic idea: find out line status and send appropriate command.
400
* If the status query fails, we do not retry more than once (we
401
* may be short on time with a dying UPS) but assume that we're
405
r = hidcmd("Q1", buf, sizeof(buf));
406
if (r < 46 || r > 47 || buf[0] != '(') r = hidcmd("Q1", buf, sizeof(buf));
408
if (r >= 46 && r <= 47 && buf[0] == '(')
411
upslogx(LOG_WARNING, "On battery, sending shutdown command...");
415
upslogx(LOG_WARNING, "On line, sending shutdown+return command...");
418
else upslogx(LOG_WARNING, "Status undetermined, assuming battery power, "
419
"sending shutdown command...");
421
hidsend(b ? "S01" : "S01R0003");
438
/* Basic idea: find out line status and send appropriate command.
439
* If the status query fails, we do not retry more than once (we
440
* may be short on time with a dying UPS) but assume that we're
444
r = hidcmd("Q1", buf, sizeof(buf));
445
if (r < 46 || r > 47 || buf[0] != '(') r = hidcmd("Q1", buf, sizeof(buf));
447
if (r >= 46 && r <= 47 && buf[0] == '(')
450
upslogx(LOG_WARNING, "On battery, sending shutdown command...");
454
upslogx(LOG_WARNING, "On line, sending shutdown+return command...");
457
else upslogx(LOG_WARNING, "Status undetermined, assuming battery power, "
458
"sending shutdown command...");
460
hidsend(b ? "S01" : "S01R0003");