1 /*
2 * $Id: hid-ups.c,v 1.13.2.2 2007-07-17 22:54:25 adk0212 Exp $
3 *
4 * Copyright (c) 2001 Vojtech Pavlik <vojtech@ucw.cz>
5 * Copyright (c) 2001 Paul Stewart <hiddev@wetlogic.net>
6 *
7 * Tweaked by Kern Sibbald <kern@sibbald.com> to learn
8 * about USB UPSes.
9 *
10 * HID UPS device test program
11 */
12
13 /*
14 * This program is free software; you can redistribute it and/or modify
15 * it under the terms of the GNU General Public License as published by
16 * the Free Software Foundation; either version 2 of the License, or
17 * (at your option) any later version.
18 *
19 * This program is distributed in the hope that it will be useful,
20 * but WITHOUT ANY WARRANTY; without even the implied warranty of
21 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
22 * GNU General Public License for more details.
23 *
24 * You should have received a copy of the GNU General Public License
25 * along with this program; if not, write to the Free Software
26 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335, USA
27 *
28 * Should you need to contact me, the author, you can do so either by
29 * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
30 * Vojtech Pavlik, Simunkova 1492, Prague 8, 182 00 Czech Republic
31 */
32
33 #define DEBUG 1 /* if set prints full reports */
34 #define TESTING 1 /* if set disables actual operation */
35
36 #define HID_MAX_USAGES 1024
37
38 #include <stdlib.h>
39 #include <string.h>
40 #include <fcntl.h>
41 #include <unistd.h>
42 #include <stdio.h>
43 #include <sys/types.h>
44 #include <sys/time.h>
45 #include <sys/ioctl.h>
46 #include <sys/signal.h>
47 #include <asm/types.h>
48 #include <linux/hiddev.h>
49 #include <errno.h>
50 #include <time.h>
51
52 #define PWRSTAT "/etc/powerstatus"
53 #define DEBOUNCE_TIMEOUT 15 /* increase this if you get false alerts */
54
55 #define UPS_USAGE 0x840000
56 #define UPS_SERIAL 0x8400fe
57 #define BAT_CHEMISTRY 0x850089
58 #define UPS_CAPACITY_MODE 0x85002c
59
60 #define UPS_SHUTDOWN_IMMINENT 0x840069
61 #define UPS_BATTERY_VOLTAGE 0x840030
62 #define UPS_BELOW_RCL 0x840042
63 #define UPS_CHARING 0x840044
64 #define UPS_DISCHARGING 0x850045
65 #define UPS_REMAINING_CAPACITY 0x850066
66 #define UPS_RUNTIME_TO_EMPTY 0x850068
67 #define UPS_AC_PRESENT 0x8500d0
68
69 #define STATE_NORMAL 0 /* unit powered */
70 #define STATE_DEBOUNCE 1 /* power failure */
71 #define STATE_BATTERY 2 /* power failure confirmed */
72
73 /* USB Vendor ID's */
74 #define Vendor_APC 0x51D
75 #define Vendor_MGE 0x463
76
77
78 #ifdef DEBUG
79 /*
80 * The type field indicates the type or units to be
81 * applied to the value.
82 */
83
84 #define T_NONE 0 /* No units */
85 #define T_INDEX 1 /* String index */
86 #define T_CAPACITY 2 /* Capacity (usually %) */
87 #define T_BITS 3 /* bit field */
88 #define T_UNITS 4 /* use units/exponent field */
89 #define T_DATE 5 /* date */
90 #define T_APCDATE 6 /* APC date format */
91
92 struct s_ups_info {
93 unsigned usage;
94 int type;
95 const char *label;
96 } ups_info[] = {
97
98 /* MGE & APC */
99 { 0x000000, T_NONE, "None" },
100 /* Page 0x84 is the Power Device Page */
101 { 0x840000, T_NONE, "UPS-Power" },
102 { 0x840001, T_INDEX, "iName" },
103 { 0x840002, T_NONE, "PresentStatus" },
104 { 0x840004, T_NONE, "UPS" },
105 { 0x840012, T_NONE, "Battery" },
106 { 0x840016, T_NONE, "PowerConverter" },
107 { 0x840018, T_NONE, "OutletSystem" },
108 { 0x840017, T_NONE, "PowerConverterID" },
109 { 0x840019, T_NONE, "OutletSystemID" },
110 { 0x84001a, T_NONE, "Input" },
111 { 0x84001c, T_NONE, "Output" },
112 { 0x84001e, T_NONE, "Flow" },
113 { 0x84001d, T_NONE, "OutputID" },
114 { 0x84001f, T_NONE, "FlowID" },
115 { 0x840020, T_NONE, "Outlet" },
116 { 0x840021, T_NONE, "OutletID" },
117 { 0x840024, T_NONE, "PowerSummary" },
118 { 0x840025, T_NONE, "PowerSummaryID" },
119 { 0x840030, T_UNITS, "Voltage" },
120 { 0x840031, T_UNITS, "Current" },
121 { 0x840032, T_UNITS, "Frequency" },
122 { 0x840033, T_UNITS, "ApparentPower" },
123 { 0x840034, T_UNITS, "ActivePower" },
124 { 0x840035, T_UNITS, "PercentLoad" },
125 { 0x840036, T_UNITS, "Temperature" },
126 { 0x840037, T_UNITS, "Humidity" },
127 { 0x840040, T_UNITS, "ConfigVoltage" },
128 { 0x840042, T_UNITS, "ConfigFrequency" },
129 { 0x840043, T_UNITS, "ConfigApparentPower" },
130 { 0x840044, T_UNITS, "ConfigActivePower" },
131 { 0x840053, T_UNITS, "LowVoltageTransfer" },
132 { 0x840054, T_UNITS, "HighVoltageTransfer" },
133 { 0x840055, T_UNITS, "DelayBeforeReboot" },
134 { 0x840056, T_UNITS, "DelayBeforeStartup" },
135 { 0x840057, T_NONE, "DelayBeforeShutdown" },
136 { 0x840058, T_NONE, "Test" },
137 { 0x84005a, T_NONE, "AudibleAlarmControl" },
138 { 0x840061, T_NONE, "Good" },
139 { 0x840062, T_NONE, "InternalFailure" },
140 { 0x840065, T_NONE, "Overload" },
141 { 0x840068, T_NONE, "ShutdownRequested" },
142 { 0x840069, T_NONE, "ShutdownImminent" },
143 { 0x84006b, T_NONE, "Switch On/Off" },
144 { 0x84006c, T_NONE, "Switchable" },
145 { 0x84006e, T_NONE, "Boost" },
146 { 0x84006f, T_NONE, "Trim" },
147 { 0x840073, T_NONE, "CommunicationLost" },
148 { 0x8400fd, T_INDEX, "iManufacturer" },
149 { 0x8400fe, T_INDEX, "iProduct" },
150 { 0x8400ff, T_INDEX, "iSerialNumber" },
151 /* Page 0x85 is the Battery System Page */
152 { 0x850000, T_NONE, "UPS-Batt" },
153 { 0x850029, T_CAPACITY, "RemainingCapacityLimit" },
154 { 0x85002a, T_UNITS, "RemainingTimeLimit" },
155 { 0x85002c, T_CAPACITY, "CapacityMode" },
156 { 0x850042, T_NONE, "BelowRemainingCapacityLimit" },
157 { 0x850043, T_NONE, "RemainingTimeLimitExpired" },
158 { 0x850044, T_NONE, "Charging" },
159 { 0x850045, T_NONE, "Discharging" },
160 { 0x85004b, T_NONE, "NeedReplacement" },
161 { 0x850058, T_NONE, "BUPHibernate" }, /* APC proprietary */
162 { 0x850066, T_CAPACITY, "RemainingCapacity" },
163 { 0x850067, T_CAPACITY, "FullChargeCapacity" },
164 { 0x850068, T_UNITS, "RunTimeToEmpty" },
165 { 0x85006b, T_NONE, "CycleCount" },
166 { 0x850080, T_NONE, "BattPackLevel" },
167 { 0x850083, T_CAPACITY, "DesignCapacity" },
168 { 0x850085, T_DATE, "ManufactureDate" },
169 { 0x850088, T_INDEX, "iDeviceName" },
170 { 0x850089, T_INDEX, "iDeviceChemistry" },
171 { 0x85008b, T_NONE, "Rechargeable" },
172 { 0x85008c, T_CAPACITY, "WarningCapacityLimit" },
173 { 0x85008d, T_CAPACITY, "CapacityGranularity1" },
174 { 0x85008e, T_CAPACITY, "CapacityGranularity2" },
175 { 0x85008f, T_INDEX, "iOEMInformation" },
176 { 0x8500d0, T_NONE, "ACPresent" },
177 { 0x8500d1, T_NONE, "BatteryPresent" },
178 { 0x8500db, T_NONE, "VoltageNotRegulated" },
179 /*
180 * Page 0x86 is reserved for Power Devices, but not defined in the HID
181 * standard. APC has defined a few usages on this page for themselves.
182 */
183 { 0x860010, T_NONE, "BUPSelfTest" }, /* APC proprietary */
184 { 0x860012, T_NONE, "BUPBattCapBeforeStartup" }, /* APC proprietary */
185 { 0x860076, T_NONE, "BUPDelayBeforeStartup" }, /* APC proprietary */
186 /* Pages 0xFF00 to 0xFFFF are vendor specific */
187 { 0xFF860005, T_NONE, "APCGeneralCollection" },
188 { 0xFF860013, T_NONE, "APC860013_SetMinReturn?" },
189 { 0xFF860016, T_APCDATE,"APCBattReplacementDate" },
190 { 0xFF860019, T_UNITS, "APCBattCapBeforeStartup" },
191 { 0xFF860023, T_NONE, "APC860023_??????" },
192 { 0xFF860024, T_NONE, "APC860024_??????" },
193 { 0xFF860025, T_NONE, "APC860025_??????" },
194 { 0xFF860026, T_NONE, "APC860026_??????" },
195 { 0xFF860029, T_NONE, "APC860029_??????" },
196 { 0xFF86002A, T_NONE, "APC86002A_??????" },
197 { 0xFF860042, T_INDEX, "APC_UPS_FirmwareRevision" },
198 { 0xFF860052, T_NONE, "APCLineFailCause" },
199 { 0xFF860060, T_BITS, "APCStatusFlag" },
200 { 0xFF860061, T_NONE, "APCSensitivity" },
201 { 0xFF860062, T_NONE, "APC860062_SetHiTransV?" },
202 { 0xFF860064, T_NONE, "APC860064_SetLoTransV?" },
203 { 0xFF860072, T_NONE, "APCPanelTest" },
204 { 0xFF860074, T_NONE, "APC860074_SetSens?" },
205 { 0xFF860076, T_UNITS, "APCShutdownAfterDelay" },
206 { 0xFF860077, T_NONE, "APC860077_SetWakeUpDelay?" },
207 { 0xFF860079, T_INDEX, "APC_USB_FirmwareRevision" },
208 { 0xFF86007C, T_NONE, "APCForceShutdown" },
209 { 0xFF86007D, T_UNITS, "APCDelayBeforeShutdown" },
210 { 0xFF86007E, T_UNITS, "APCDelayBeforeStartup" },
211 { 0xFF8600FC, T_UNITS, "ModbusRTURx" },
212 { 0xFF8600FD, T_UNITS, "ModbusRTUTx" },
213
214 };
215 #define UPS_INFO_SZ (sizeof(ups_info)/sizeof(ups_info[0]))
216
217 const char *reports[] = { "Unknown", "Input", "Output", "Feature" };
218
219 static int CapacityMode = 2; /* default = % */
220
221 char unknown[24];
222
223 #define MADDR "stewart@wetlogic.net"
224
log_status(const char * msg)225 void log_status(const char *msg) {
226 #ifndef TESTING
227 char buf[256];
228 printf("[Log message \"%s\"]\n", msg);
229 sprintf(buf, "/bin/echo %s | /bin/mail -s \"UPS System\" %s", msg, MADDR);
230 system(buf);
231 #else
232 printf("[Log message \"%s\"]\n", msg);
233 #endif
234 }
235
info(unsigned int detail)236 static const char* info(unsigned int detail) {
237 unsigned int i;
238
239 for (i = 0; i < UPS_INFO_SZ; i++) {
240 if (ups_info[i].usage == detail) {
241 return ups_info[i].label;
242 }
243 }
244
245 sprintf(unknown, "[%06x]", detail);
246
247 return unknown;
248 }
249
info_entry(unsigned int detail)250 static struct s_ups_info *info_entry(unsigned int detail) {
251 unsigned int i;
252 static struct s_ups_info info = {0, T_NONE, unknown};
253
254 for (i = 0; i < UPS_INFO_SZ; i++) {
255 if (ups_info[i].usage == detail) {
256 return &ups_info[i];
257 }
258 }
259 sprintf(unknown, "[%06x]", detail);
260 return &info;
261 }
262
263 #else /* DEBUG */
264 #define log_status(s)
265 #endif /* DEBUG */
266
267 /* Tell init the power has either gone or is back. */
powerfail(int state)268 void powerfail(int state) {
269 #ifndef TESTING
270 int fd;
271
272 /* Create an info file needed by init to shutdown/cancel shutdown */
273 unlink(PWRSTAT);
274 if ((fd = open(PWRSTAT, O_CREAT|O_WRONLY, 0644)) >= 0) {
275 if (state > 0)
276 write(fd, "FAIL\n", 5);
277 else if (state < 0)
278 write(fd, "LOW\n", 4);
279 else
280 write(fd, "OK\n", 3);
281 close(fd);
282 }
283 kill(1, SIGPWR);
284 #else
285 printf("We are in powerfail() with state=%d ", state);
286 if (state > 0)
287 printf("POWER FAILURE\n");
288 else if (state < 0)
289 printf("BATTERY LOW\n");
290 else
291 printf("OK\n");
292 #endif
293
294 }
295
find_application(int fd,unsigned usage)296 static inline int find_application(int fd, unsigned usage) {
297 int i = 0, ret;
298 while ((ret = ioctl(fd, HIDIOCAPPLICATION, i)) > 0 &&
299 (ret & 0xffff0000) != (usage & 0xffff0000)) i++;
300 return ((ret & 0xffff000) == (usage & 0xffff0000));
301 }
302
303 /*
304 * Get a string from the device given the string's index
305 */
get_string(int fd,int sindex)306 static const char *get_string(int fd, int sindex) {
307 static struct hiddev_string_descriptor sdesc;
308 static char buf[200];
309
310 if (sindex == 0) {
311 return "";
312 }
313 sdesc.index = sindex;
314 if (ioctl(fd, HIDIOCGSTRING, &sdesc) < 0) {
315 sprintf(buf, "String index %d returned ERR=%s\n", sindex,
316 strerror(errno));
317 return buf;
318 }
319 return sdesc.value;
320 }
321
322 /*
323 * Give units code and exponent, returns string
324 * describing the units used. (doesn't work for percentages).
325 */
get_units(unsigned unit,int exponent)326 static const char *get_units(unsigned unit, int exponent) {
327 static char buf[200];
328
329 if (exponent > 7) {
330 exponent = exponent - 16;
331 }
332 switch (unit) {
333 case 1: /* special kludge for CapacityMode */
334 switch (exponent) {
335 case 0:
336 return "maH";
337 case 1:
338 return "mwH";
339 case 2:
340 return "percent";
341 case 3:
342 return "boolean";
343 default:
344 return "";
345 }
346 case 0x00F0D121:
347 if (exponent == 7) {
348 return "Volts";
349 } else if (exponent == 5) {
350 return "CentiVolts";
351 } else if (exponent == 6) {
352 return "DeciVolts";
353 } else {
354 sprintf(buf, "Volts with %d exponent", exponent);
355 return buf;
356 }
357 case 0x00100001:
358 if (exponent == -2) {
359 return "CentiAmps";
360 } else if (exponent == 0) {
361 return "Amps";
362 } else {
363 sprintf(buf, "Amps with %d exponent", exponent);
364 return buf;
365 }
366 case 0xF001:
367 if (exponent == 0) {
368 return "Hertz";
369 } else if (exponent == -2) {
370 return "CentiHertz";
371 } else {
372 sprintf(buf, "Hertz with %d exponent", exponent);
373 return buf;
374 }
375 case 0x1001:
376 if (exponent == 0) {
377 return "Seconds";
378 } else {
379 sprintf(buf, "Seconds with %d exponent", exponent);
380 return buf;
381 }
382 case 0xD121:
383 return "Watts";
384 case 0x010001:
385 if (exponent == 0) {
386 return "Degrees K";
387 } else {
388 sprintf(buf, "Degrees K with %d exponent", exponent);
389 return buf;
390 }
391 case 0x0101001:
392 return "AmpSecs";
393 case 0:
394 return "";
395 default:
396 sprintf(buf, "0x%x", unit);
397 return buf;
398 }
399 }
400
401
402 static char evdev[50];
403
404 static int vendor = 0;
405
main(int argc,char ** argv)406 int main (int argc, char **argv) {
407 time_t start_seconds;
408 int fd = -1, rd, j, RemainingCapacity;
409 unsigned int i;
410 struct hiddev_event ev[64];
411 struct hiddev_devinfo dinfo;
412 char name[256] = "Unknown";
413 int state = 0;
414 fd_set fdset;
415 struct timeval timev, *tv = NULL;
416 struct hiddev_usage_ref uref;
417
418 if (argc < 2) {
419 /* deal with either standard location or Red Hat's */
420 const char *hid_dirs[] = {"/dev/usb/hid", "/dev/usb","/dev"};
421 for (i = 0; i < sizeof(hid_dirs)/sizeof(hid_dirs[0]); i++) {
422 for (j = 0; j < 4; j++) {
423 sprintf(evdev, "%s/hiddev%d", hid_dirs[i], j);
424 if ((fd = open(evdev, O_RDONLY)) < 0) {
425 if (errno == EACCES) {
426 fprintf(stderr, "No permission, try this as root.\n");
427 exit(1);
428 }
429 } else {
430 if (find_application(fd, UPS_USAGE)) goto foundit;
431 close(fd);
432 }
433 }
434 }
435 fprintf(stderr, "Couldn't find USB UPS device, check your /dev.\n");
436 exit(1);
437 foundit:
438 printf("Found UPS at %s\n", evdev);
439 } else {
440 strncpy(evdev, argv[argc -1], sizeof(evdev)-1);
441 printf("Found UPS at %s\n", evdev);
442 if ((fd = open(evdev, O_RDONLY)) < 0) {
443 perror("hiddev open");
444 exit(1);
445 }
446 if (!find_application(fd, UPS_USAGE)) {
447 fprintf(stderr, "%s is not a UPS\n", argv[argc - 1]);
448 exit(1);
449 }
450 }
451
452 #ifdef DEBUG
453 {
454 unsigned version;
455
456 ioctl(fd, HIDIOCGVERSION, &version);
457 printf("hiddev driver version is %d.%d.%d\n",
458 version >> 16, (version >> 8) & 0xff, version & 0xff);
459
460 ioctl(fd, HIDIOCGDEVINFO, &dinfo);
461 printf("HID: vendor 0x%x product 0x%x version 0x%x ",
462 dinfo.vendor, dinfo.product & 0xffff, dinfo.version);
463 printf("app %s", info(ioctl(fd, HIDIOCAPPLICATION, 0)));
464 for (i = 1; i < dinfo.num_applications; i++)
465 printf(", %s", info(ioctl(fd, HIDIOCAPPLICATION, i)));
466 printf("\n");
467 printf("HID: bus: %d devnum: %d ifnum: %d\n",
468 dinfo.busnum, dinfo.devnum, dinfo.ifnum);
469 vendor = dinfo.vendor;
470
471 }
472 #endif
473
474 ioctl(fd, HIDIOCINITREPORT, 0);
475 ioctl(fd, HIDIOCGNAME(sizeof(name)), name);
476 printf("UPS HID device name: \"%s\"\n", name);
477
478
479 memset(&uref, 0, sizeof(uref));
480 uref.report_type = HID_REPORT_TYPE_FEATURE;
481 uref.report_id = HID_REPORT_ID_UNKNOWN;
482 uref.usage_code = BAT_CHEMISTRY;
483 if (ioctl(fd, HIDIOCGUSAGE, &uref) == 0) {
484 printf("Battery Chemistry: \"%s\" (%d)\n", get_string(fd, uref.value),
485 uref.value);
486 }
487
488 memset(&uref, 0, sizeof(uref));
489 uref.report_type = HID_REPORT_TYPE_FEATURE;
490 uref.report_id = HID_REPORT_ID_UNKNOWN;
491 uref.usage_code = UPS_CAPACITY_MODE;
492 if (ioctl(fd, HIDIOCGUSAGE, &uref) == 0) {
493 CapacityMode = uref.value;
494 }
495
496 #ifdef DEBUG
497 /* To traverse the report descriptor info
498 */
499
500 {
501 struct hiddev_report_info rinfo;
502 struct hiddev_field_info finfo;
503 struct hiddev_usage_ref uref;
504 int rtype;
505 unsigned int i, j;
506
507 for (rtype = HID_REPORT_TYPE_MIN; rtype <= HID_REPORT_TYPE_MAX;
508 rtype++) {
509 rinfo.report_type = rtype;
510 rinfo.report_id = HID_REPORT_ID_FIRST;
511 while (ioctl(fd, HIDIOCGREPORTINFO, &rinfo) >= 0) {
512 printf("\n%sReport %d\n",
513 reports[rinfo.report_type], rinfo.report_id);
514
515 for (i = 0; i < rinfo.num_fields; i++) {
516 struct s_ups_info *p;
517
518 memset(&finfo, 0, sizeof(finfo));
519 finfo.report_type = rinfo.report_type;
520 finfo.report_id = rinfo.report_id;
521 finfo.field_index = i;
522 ioctl(fd, HIDIOCGFIELDINFO, &finfo);
523 printf(" Field %d, app %s, phys %s, log %s\n",
524 i,
525 info(finfo.application), info(finfo.physical),
526 info(finfo.logical));
527
528 memset(&uref, 0, sizeof(uref));
529 for (j = 0; j < finfo.maxusage; j++) {
530 unsigned unit;
531 int exponent;
532 int v, yy, mm, dd;
533 uref.report_type = finfo.report_type;
534 uref.report_id = finfo.report_id;
535 uref.field_index = i;
536 uref.usage_index = j;
537 ioctl(fd, HIDIOCGUCODE, &uref);
538 ioctl(fd, HIDIOCGUSAGE, &uref);
539 p = info_entry(uref.usage_code);
540 switch (p->type) {
541 case T_CAPACITY:
542 unit = 1;
543 printf("Exponent %d lost.\n", finfo.unit_exponent);
544 exponent = CapacityMode;
545 break;
546 case T_UNITS:
547 unit = finfo.unit;
548 exponent = finfo.unit_exponent;
549 break;
550 default:
551 unit = 0;
552 exponent = 0;
553 break;
554 }
555
556 printf(" Usage %d, %s = %d %s", j, p->label, uref.value,
557 get_units(unit, exponent));
558
559 switch (p->type) {
560 case T_INDEX:
561 printf(" %s\n", get_string(fd, uref.value));
562 break;
563 case T_BITS: /* binary bits */
564 printf(" 0x%x\n", uref.value);
565 break;
566 case T_DATE: /* packed integer date */
567 printf(" %4d-%02d-%02d\n", (uref.value >> 9) + 1980,
568 (uref.value >> 5) & 0xF, uref.value & 0x1F);
569 break;
570 case T_APCDATE: /* APC date */
571 v = uref.value;
572 yy = ((v>>4) & 0xF)*10 + (v&0xF) + 2000;
573 v >>= 8;
574 dd = ((v>>4) & 0xF)*10 + (v&0xF);
575 v >>= 8;
576 mm = ((v>>4) & 0xF)*10 + (v&0xF);
577 printf(" %4d-%02d-%02d\n", yy, mm, dd);
578 default:
579 printf("\n");
580 break;
581 }
582 }
583 }
584 rinfo.report_id |= HID_REPORT_ID_NEXT;
585 }
586 }
587 }
588
589 printf("\nWaiting for events ... (interrupt to exit)\n");
590 fflush(stdout);
591
592 #endif
593
594 start_seconds = time(NULL);
595 FD_ZERO(&fdset);
596 while (1) {
597 if (fd < 0) {
598 sleep(5);
599 fd = open(evdev, O_RDONLY);
600 if (fd < 0) {
601 perror("\nOpen error");
602 continue;
603 }
604 if (!find_application(fd, UPS_USAGE)) {
605 fprintf(stderr, "\nCould not find_application.\n");
606 close(fd);
607 fd = -1;
608 }
609 continue;
610 }
611 switch (state) {
612 case STATE_NORMAL:
613 tv = NULL;
614 break;
615 case STATE_BATTERY:
616 case STATE_DEBOUNCE:
617 timev.tv_sec = DEBOUNCE_TIMEOUT;
618 timev.tv_usec = 0;
619 tv = &timev;
620 break;
621 }
622
623 FD_SET(fd, &fdset);
624 rd = select(fd+1, &fdset, NULL, NULL, tv);
625
626 if (rd > 0) {
627 rd = read(fd, ev, sizeof(ev));
628 if (rd < (int) sizeof(ev[0])) {
629 if (rd < 0)
630 perror("\nevtest: error reading");
631 else
632 fprintf(stderr, "\nevtest: got short read from device!\n");
633 // exit (1);
634 close(fd);
635 fd = -1;
636 continue;
637 } else {
638 printf("time %lu\n", time(NULL) - start_seconds);
639 }
640
641 for (i = 0; i < rd / sizeof(ev[0]); i++) {
642 #ifdef DEBUG
643 printf("Event: %s = %d\n",
644 info(ev[i].hid), ev[i].value);
645 #endif /* DEBUG */
646
647 if (ev[i].hid == UPS_SHUTDOWN_IMMINENT && ev[i].value == 1) {
648 log_status("UPS shutdown imminent!");
649 powerfail(-1);
650 state = STATE_BATTERY;
651 RemainingCapacity = -1;
652 }
653 switch (state) {
654 case STATE_BATTERY:
655 if (ev[i].hid == UPS_DISCHARGING && ev[i].value == 0) {
656 log_status("System back on AC power");
657 powerfail(0);
658 state = STATE_NORMAL;
659 RemainingCapacity = -1;
660 }
661 break;
662 case STATE_DEBOUNCE:
663 if (ev[i].hid == UPS_DISCHARGING && ev[i].value == 0) {
664 state = STATE_NORMAL;
665 RemainingCapacity = -1;
666 }
667 break;
668 case STATE_NORMAL:
669 if (ev[i].hid == UPS_DISCHARGING && ev[i].value == 1) {
670 state = STATE_DEBOUNCE;
671 RemainingCapacity = -1;
672 }
673 break;
674 }
675 }
676 } else {
677 /* Our timer has expired */
678 switch (state) {
679 case STATE_DEBOUNCE:
680 log_status("System switched to battery power");
681 state = STATE_BATTERY;
682 powerfail(1);
683 break;
684 default:
685 break;
686 }
687 }
688 fflush(stdout);
689 }
690 }
691