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