3 * apcsmart.c -- The decoding of the chatty little beasts.
4 * THE LOOK-A-LIKE ( UPSlink(tm) Language )
6 * apcupsd.c -- Simple Daemon to catch power failure signals from a
7 * BackUPS, BackUPS Pro, or SmartUPS (from APCC).
8 * -- Now SmartMode support for SmartUPS and BackUPS Pro.
10 * Copyright (C) 1996-99 Andre M. Hedrick <andre@suse.com>
11 * All rights reserved.
16 * Parts of the information below was taken from apcd.c & apcd.h
18 * Definitons file for APC SmartUPS daemon
20 * Copyright (c) 1995 Pavel Korensky
23 * IN NO EVENT SHALL PAVEL KORENSKY BE LIABLE TO ANY PARTY FOR
24 * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT
25 * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF PAVEL KORENSKY
26 * HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 * PAVEL KORENSKY SPECIFICALLY DISCLAIMS ANY WARRANTIES, INCLUDING, BUT NOT
29 * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
30 * A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS ON AN "AS IS"
31 * BASIS, AND PAVEL KORENSKY HAS NO OBLIGATION TO PROVIDE MAINTENANCE, SUPPORT,
32 * UPDATES, ENHANCEMENTS, OR MODIFICATIONS.
34 * Pavel Korensky pavelk@dator3.anet.cz
38 * P.S. I have absolutely no connection with company APC. I didn't sign any
39 * non-disclosure agreement and I didn't got the protocol description anywhere.
40 * The whole protocol decoding was made with a small program for capturing
41 * serial data on the line. So, I think that everybody can use this software
42 * without any problem.
47 Copyright (C) 1999-2004 Kern Sibbald
49 This program is free software; you can redistribute it and/or
50 modify it under the terms of the GNU General Public License as
51 published by the Free Software Foundation; either version 2 of
52 the License, or (at your option) any later version.
54 This program is distributed in the hope that it will be useful,
55 but WITHOUT ANY WARRANTY; without even the implied warranty of
56 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
57 General Public License for more details.
59 You should have received a copy of the GNU General Public
60 License along with this program; if not, write to the Free
61 Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
70 /* How long to wait before declaring commlost */
71 #define COMMLOST_TIMEOUT_MS (20*1000)
73 /* Convert UPS response to enum and string */
74 static SelfTestResult
decode_testresult(char* str
)
78 * "OK" - good battery,
79 * "BT" - failed due to insufficient capacity,
80 * "NG" - failed due to overload,
81 * "NO" - no results available (no test performed in last 5 minutes)
83 if (str
[0] == 'O' && str
[1] == 'K')
85 else if (str
[0] == 'B' && str
[1] == 'T')
87 else if (str
[0] == 'N' && str
[1] == 'G')
93 /* Convert UPS response to enum and string */
94 static LastXferCause
decode_lastxfer(char *str
)
96 Dmsg1(80, "Transfer reason: %c\n", *str
);
104 return XFER_OVERVOLT
;
106 return XFER_UNDERVOLT
;
108 return XFER_NOTCHSPIKE
;
114 return XFER_SELFTEST
;
120 int apc_enable(UPSINFO
*ups
)
122 /* Enable APC Smart UPS */
123 smart_poll('Y', ups
);
124 smart_poll('Y', ups
);
128 /*********************************************************************
130 * Send a charcter to the UPS and get
131 * its response. Returns a pointer to the response string.
134 char *smart_poll(char cmd
, UPSINFO
*ups
)
136 static char answer
[2000];
143 /* Don't retry Y/SM command */
144 retry
= (cmd
== 'Y') ? 0 : 2;
147 write(ups
->fd
, &cmd
, 1);
148 stat
= getline(answer
, sizeof answer
, ups
);
150 /* If nothing returned, the link is probably down */
151 if (*answer
== 0 && stat
== FAILURE
) {
152 UPSlinkCheck(ups
); /* wait for link to come up */
153 *answer
= 0; /* UPSlinkCheck invokes us recursively, so clean up */
155 } while (*answer
== 0 && stat
== FAILURE
&& retry
--);
160 int writechar(char a
, UPSINFO
*ups
)
162 return write(ups
->fd
, &a
, 1);
166 * If s == NULL we are just waiting on FD for status changes.
167 * If s != NULL we are asking the UPS to tell us the value of something.
169 * If s == NULL there is a much more fine-grained locking.
171 int getline(char *s
, int len
, UPSINFO
*ups
)
180 wait
= TIMER_FAST
; /* 1 sec, expect fast response */
182 wait
= ups
->wait_time
;
185 /* Set read() timeout since we have no select() support. */
188 HANDLE h
= (HANDLE
)_get_osfhandle(ups
->fd
);
189 ct
.ReadIntervalTimeout
= MAXDWORD
;
190 ct
.ReadTotalTimeoutMultiplier
= MAXDWORD
;
191 ct
.ReadTotalTimeoutConstant
= wait
* 1000;
192 ct
.WriteTotalTimeoutMultiplier
= 0;
193 ct
.WriteTotalTimeoutConstant
= 0;
194 SetCommTimeouts(h
, &ct
);
199 #if !defined(HAVE_MINGW)
204 FD_SET(ups
->fd
, &rfds
);
209 retval
= select((ups
->fd
) + 1, &rfds
, NULL
, NULL
, &tv
);
212 case 0: /* No chars available in TIMER seconds. */
215 if (errno
== EINTR
|| errno
== EAGAIN
) { /* assume SIGCHLD */
217 } else if (errno
== EBADF
) {
218 return FAILURE
; /* We're probably shutting down */
220 Error_abort1("Select error on UPS FD. %s\n", strerror(errno
));
228 retval
= read(ups
->fd
, &c
, 1);
229 } while (retval
== -1 && (errno
== EAGAIN
|| errno
== EINTR
));
236 * Here we can be called in two ways:
239 * The shm lock is not held so we must hold it here.
242 * We are called from a routine that have
243 * already held the shm lock so no need to hold it
244 * another time. Simply update the UPS structure
245 * fields and the shm will be updated when
246 * write_unlock is called by the calling
249 * If something changes on the UPS, a special character is
250 * sent over the serial line but no \n\r sequence is sent:
251 * only a single character. This way if s == NULL, if we
252 * receive a character like this we must return immediately
253 * and not wait for a string completion.
255 case UPS_ON_BATT
: /* UPS_ON_BATT = '!' */
259 Dmsg0(80, "Got UPS ON BATT.\n");
265 case UPS_REPLACE_BATTERY
: /* UPS_REPLACE_BATTERY = '#' */
268 ups
->set_replacebatt();
269 Dmsg0(80, "Got UPS REPLACE_BATT.\n");
275 case UPS_ON_LINE
: /* UPS_ON_LINE = '$' */
279 Dmsg0(80, "Got UPS ON LINE.\n");
285 case BATT_LOW
: /* BATT_LOW = '%' */
289 Dmsg0(80, "Got UPS BATT_LOW.\n");
295 case BATT_OK
: /* BATT_OK = '+' */
298 ups
->clear_battlow();
299 Dmsg0(80, "Got UPS BATT_OK.\n");
306 case UPS_EPROM_CHANGE
: /* UPS_EPROM_CHANGE = '|' */
307 case UPS_TRAILOR
: /* UPS_TRAILOR = ':' */
310 /* NOTE: The UPS terminates what it sends to us
311 * with a \r\n. Thus the line feed signals the
312 * end of what we are to receive.
314 case UPS_LF
: /* UPS_LF = '\n' */
316 ending
= 1; /* This what we waited for */
318 case UPS_CR
: /* UPS_CR = '\r' */
325 ending
= 1; /* no more room in buffer */
337 /*********************************************************************/
338 /* Note this routine MUST be called with the UPS write lock held! */
339 void UPSlinkCheck(UPSINFO
*ups
)
341 struct timeval now
, prev
, start
;
342 static int linkcheck
= FALSE
;
347 linkcheck
= TRUE
; /* prevent recursion */
349 tcflush(ups
->fd
, TCIOFLUSH
);
350 if (strcmp(smart_poll('Y', ups
), "SM") == 0) {
352 ups
->clear_commlost();
358 gettimeofday(&start
, NULL
);
361 tcflush(ups
->fd
, TCIOFLUSH
);
362 while (strcmp(smart_poll('Y', ups
), "SM") != 0) {
363 /* Declare commlost only if COMMLOST_TIMEOUT_MS has expired */
364 gettimeofday(&now
, NULL
);
365 if (TV_DIFF_MS(start
, now
) >= COMMLOST_TIMEOUT_MS
) {
366 /* Generate commlost event if we've not done so yet */
367 if (!ups
->is_commlost()) {
369 generate_event(ups
, CMDCOMMFAILURE
);
373 /* Log an event every 10 minutes */
374 if (TV_DIFF_MS(prev
, now
) >= 10*60*1000) {
375 log_event(ups
, event_msg
[CMDCOMMFAILURE
].level
,
376 event_msg
[CMDCOMMFAILURE
].msg
);
382 * This sleep should not be necessary since the smart_poll()
383 * routine normally waits TIMER_FAST (1) seconds. However,
384 * in case the serial port is broken and generating spurious
385 * characters, we sleep to reduce CPU consumption.
388 tcflush(ups
->fd
, TCIOFLUSH
);
393 if (ups
->is_commlost()) {
394 ups
->clear_commlost();
395 generate_event(ups
, CMDCOMMOK
);
401 /*********************************************************************
403 * This subroutine is called to load our shared memory with
404 * information that is changing inside the UPS depending
405 * on the state of the UPS and the mains power.
407 int apcsmart_ups_read_volatile_data(UPSINFO
*ups
)
413 * We need it for self-test start time.
419 UPSlinkCheck(ups
); /* make sure serial port is working */
421 ups
->poll_time
= time(NULL
); /* save time stamp */
424 if (ups
->UPS_Cap
[CI_STATUS
]) {
426 int retries
= 5; /* Number of retries on status read */
429 answer
= smart_poll(ups
->UPS_Cmd
[CI_STATUS
], ups
);
430 Dmsg1(80, "Got CI_STATUS: %s\n", answer
);
431 strncpy(status
, answer
, sizeof(status
));
434 * The Status command may return "SM" probably because firmware
435 * is in a state where it still didn't updated its internal status
436 * register. In this case retry to read the register. To be sure
437 * not to get stuck here, we retry only 5 times.
441 * If this fails, apcupsd may not be able to detect a status
442 * change and will have unpredictable behavior. This will be fixed
443 * once we will handle correctly the own apcupsd Status word.
445 if (status
[0] == 'S' && status
[1] == 'M' && (retries
-- > 0))
448 ups
->Status
&= ~0xFF; /* clear APC byte */
449 ups
->Status
|= strtoul(status
, NULL
, 16) & 0xFF; /* set APC byte */
452 /* ONBATT_STATUS_FLAG -- line quality */
453 if (ups
->UPS_Cap
[CI_LQUAL
]) {
454 answer
= smart_poll(ups
->UPS_Cmd
[CI_LQUAL
], ups
);
455 Dmsg1(80, "Got CI_LQUAL: %s\n", answer
);
456 astrncpy(ups
->linequal
, answer
, sizeof(ups
->linequal
));
459 /* Reason for last transfer to batteries */
460 if (ups
->UPS_Cap
[CI_WHY_BATT
]) {
461 answer
= smart_poll(ups
->UPS_Cmd
[CI_WHY_BATT
], ups
);
462 Dmsg1(80, "Got CI_WHY_BATT: %s\n", answer
);
463 ups
->lastxfer
= decode_lastxfer(answer
);
467 * See if this is a self test rather than power failure
469 * When we will be ready we will copy the code below inside
470 * the driver entry point, for performing this check inside the
475 /* Results of last self test */
476 if (ups
->UPS_Cap
[CI_ST_STAT
]) {
477 answer
= smart_poll(ups
->UPS_Cmd
[CI_ST_STAT
], ups
);
478 Dmsg1(80, "Got CI_ST_STAT: %s\n", answer
);
479 ups
->testresult
= decode_testresult(answer
);
483 if (ups
->UPS_Cap
[CI_VLINE
]) {
484 answer
= smart_poll(ups
->UPS_Cmd
[CI_VLINE
], ups
);
485 Dmsg1(80, "Got CI_VLINE: %s\n", answer
);
486 ups
->LineVoltage
= atof(answer
);
490 if (ups
->UPS_Cap
[CI_VMAX
]) {
491 answer
= smart_poll(ups
->UPS_Cmd
[CI_VMAX
], ups
);
492 Dmsg1(80, "Got CI_VMAX: %s\n", answer
);
493 ups
->LineMax
= atof(answer
);
497 if (ups
->UPS_Cap
[CI_VMIN
]) {
498 answer
= smart_poll(ups
->UPS_Cmd
[CI_VMIN
], ups
);
499 Dmsg1(80, "Got CI_VMIN: %s\n", answer
);
500 ups
->LineMin
= atof(answer
);
504 if (ups
->UPS_Cap
[CI_VOUT
]) {
505 answer
= smart_poll(ups
->UPS_Cmd
[CI_VOUT
], ups
);
506 Dmsg1(80, "Got CI_VOUT: %s\n", answer
);
507 ups
->OutputVoltage
= atof(answer
);
510 /* BATT_FULL Battery level percentage */
511 if (ups
->UPS_Cap
[CI_BATTLEV
]) {
512 answer
= smart_poll(ups
->UPS_Cmd
[CI_BATTLEV
], ups
);
513 Dmsg1(80, "Got CI_BATTLEV: %s\n", answer
);
514 ups
->BattChg
= atof(answer
);
518 if (ups
->UPS_Cap
[CI_VBATT
]) {
519 answer
= smart_poll(ups
->UPS_Cmd
[CI_VBATT
], ups
);
520 Dmsg1(80, "Got CI_VBATT: %s\n", answer
);
521 ups
->BattVoltage
= atof(answer
);
525 if (ups
->UPS_Cap
[CI_LOAD
]) {
526 answer
= smart_poll(ups
->UPS_Cmd
[CI_LOAD
], ups
);
527 Dmsg1(80, "Got CI_LOAD: %s\n", answer
);
528 ups
->UPSLoad
= atof(answer
);
532 if (ups
->UPS_Cap
[CI_FREQ
]) {
533 answer
= smart_poll(ups
->UPS_Cmd
[CI_FREQ
], ups
);
534 Dmsg1(80, "Got CI_FREQ: %s\n", answer
);
535 ups
->LineFreq
= atof(answer
);
538 /* UPS_RUNTIME_LEFT */
539 if (ups
->UPS_Cap
[CI_RUNTIM
]) {
540 answer
= smart_poll(ups
->UPS_Cmd
[CI_RUNTIM
], ups
);
541 Dmsg1(80, "Got CI_RUNTIM: %s\n", answer
);
542 ups
->TimeLeft
= atof(answer
);
546 if (ups
->UPS_Cap
[CI_ITEMP
]) {
547 answer
= smart_poll(ups
->UPS_Cmd
[CI_ITEMP
], ups
);
548 Dmsg1(80, "Got CI_ITEMP: %s\n", answer
);
549 ups
->UPSTemp
= atof(answer
);
552 /* DIP_SWITCH_SETTINGS */
553 if (ups
->UPS_Cap
[CI_DIPSW
]) {
554 answer
= smart_poll(ups
->UPS_Cmd
[CI_DIPSW
], ups
);
555 Dmsg1(80, "Got CI_DIPSW: %s\n", answer
);
556 ups
->dipsw
= strtoul(answer
, NULL
, 16);
560 if (ups
->UPS_Cap
[CI_REG1
]) {
561 answer
= smart_poll(ups
->UPS_Cmd
[CI_REG1
], ups
);
562 Dmsg1(80, "Got CI_REG1: %s\n", answer
);
563 ups
->reg1
= strtoul(answer
, NULL
, 16);
567 if (ups
->UPS_Cap
[CI_REG2
]) {
568 answer
= smart_poll(ups
->UPS_Cmd
[CI_REG2
], ups
);
569 Dmsg1(80, "Got CI_REG2: %s\n", answer
);
570 ups
->reg2
= strtoul(answer
, NULL
, 16);
571 ups
->set_battpresent(!(ups
->reg2
& 0x20));
575 if (ups
->UPS_Cap
[CI_REG3
]) {
576 answer
= smart_poll(ups
->UPS_Cmd
[CI_REG3
], ups
);
577 Dmsg1(80, "Got CI_REG3: %s\n", answer
);
578 ups
->reg3
= strtoul(answer
, NULL
, 16);
581 /* Humidity percentage */
582 if (ups
->UPS_Cap
[CI_HUMID
]) {
583 answer
= smart_poll(ups
->UPS_Cmd
[CI_HUMID
], ups
);
584 Dmsg1(80, "Got CI_HUMID: %s\n", answer
);
585 ups
->humidity
= atof(answer
);
588 /* Ambient temperature */
589 if (ups
->UPS_Cap
[CI_ATEMP
]) {
590 answer
= smart_poll(ups
->UPS_Cmd
[CI_ATEMP
], ups
);
591 Dmsg1(80, "Got CI_ATEMP: %s\n", answer
);
592 ups
->ambtemp
= atof(answer
);
595 /* Hours since self test */
596 if (ups
->UPS_Cap
[CI_ST_TIME
]) {
597 answer
= smart_poll(ups
->UPS_Cmd
[CI_ST_TIME
], ups
);
598 Dmsg1(80, "Got CI_ST_TIME: %s\n", answer
);
599 ups
->LastSTTime
= atof(answer
);
602 apc_enable(ups
); /* reenable APC serial UPS */
609 /*********************************************************************
611 * This subroutine is called to load our shared memory with
612 * information that is static inside the UPS. Hence it
613 * normally would only be called once when starting up the
616 int apcsmart_ups_read_static_data(UPSINFO
*ups
)
620 /* Everything from here on down is non-volitile, that is
621 * we do not expect it to change while the UPS is running
622 * unless we explicitly change it.
626 if (ups
->UPS_Cap
[CI_SENS
]) {
627 answer
= smart_poll(ups
->UPS_Cmd
[CI_SENS
], ups
);
628 Dmsg1(80, "Got CI_SENS: %s\n", answer
);
629 astrncpy(ups
->sensitivity
, answer
, sizeof(ups
->sensitivity
));
633 if (ups
->UPS_Cap
[CI_DWAKE
]) {
634 answer
= smart_poll(ups
->UPS_Cmd
[CI_DWAKE
], ups
);
635 Dmsg1(80, "Got CI_DWAKE: %s\n", answer
);
636 ups
->dwake
= (int)atof(answer
);
640 if (ups
->UPS_Cap
[CI_DSHUTD
]) {
641 answer
= smart_poll(ups
->UPS_Cmd
[CI_DSHUTD
], ups
);
642 Dmsg1(80, "Got CI_DSHUTD: %s\n", answer
);
643 ups
->dshutd
= (int)atof(answer
);
646 /* LOW_TRANSFER_LEVEL */
647 if (ups
->UPS_Cap
[CI_LTRANS
]) {
648 answer
= smart_poll(ups
->UPS_Cmd
[CI_LTRANS
], ups
);
649 Dmsg1(80, "Got CI_LTRANS: %s\n", answer
);
650 ups
->lotrans
= (int)atof(answer
);
653 /* HIGH_TRANSFER_LEVEL */
654 if (ups
->UPS_Cap
[CI_HTRANS
]) {
655 answer
= smart_poll(ups
->UPS_Cmd
[CI_HTRANS
], ups
);
656 Dmsg1(80, "Got CI_HTRANS: %s\n", answer
);
657 ups
->hitrans
= (int)atof(answer
);
660 /* UPS_BATT_CAP_RETURN */
661 if (ups
->UPS_Cap
[CI_RETPCT
]) {
662 answer
= smart_poll(ups
->UPS_Cmd
[CI_RETPCT
], ups
);
663 Dmsg1(80, "Got CI_RETPCT: %s\n", answer
);
664 ups
->rtnpct
= (int)atof(answer
);
668 if (ups
->UPS_Cap
[CI_DALARM
]) {
669 answer
= smart_poll(ups
->UPS_Cmd
[CI_DALARM
], ups
);
670 Dmsg1(80, "Got CI_DALARM: %s\n", answer
);
671 astrncpy(ups
->beepstate
, answer
, sizeof(ups
->beepstate
));
674 /* LOWBATT_SHUTDOWN_LEVEL */
675 if (ups
->UPS_Cap
[CI_DLBATT
]) {
676 answer
= smart_poll(ups
->UPS_Cmd
[CI_DLBATT
], ups
);
677 Dmsg1(80, "Got CI_DLBATT: %s\n", answer
);
678 ups
->dlowbatt
= (int)atof(answer
);
682 if (ups
->upsname
[0] == 0 && ups
->UPS_Cap
[CI_IDEN
]) {
683 answer
= smart_poll(ups
->UPS_Cmd
[CI_IDEN
], ups
);
684 Dmsg1(80, "Got CI_IDEN: %s\n", answer
);
685 astrncpy(ups
->upsname
, answer
, sizeof(ups
->upsname
));
689 if (ups
->UPS_Cap
[CI_STESTI
]) {
690 answer
= smart_poll(ups
->UPS_Cmd
[CI_STESTI
], ups
);
691 Dmsg1(80, "Got CI_STESTI: %s\n", answer
);
692 astrncpy(ups
->selftest
, answer
, sizeof(ups
->selftest
));
695 /* UPS_MANUFACTURE_DATE */
696 if (ups
->UPS_Cap
[CI_MANDAT
]) {
697 answer
= smart_poll(ups
->UPS_Cmd
[CI_MANDAT
], ups
);
698 Dmsg1(80, "Got CI_MANDAT: %s\n", answer
);
699 astrncpy(ups
->birth
, answer
, sizeof(ups
->birth
));
702 /* UPS_SERIAL_NUMBER */
703 if (ups
->UPS_Cap
[CI_SERNO
]) {
704 answer
= smart_poll(ups
->UPS_Cmd
[CI_SERNO
], ups
);
705 Dmsg1(80, "Got CI_SERNO: %s\n", answer
);
706 astrncpy(ups
->serial
, answer
, sizeof(ups
->serial
));
709 /* UPS_BATTERY_REPLACE */
710 if (ups
->UPS_Cap
[CI_BATTDAT
]) {
711 answer
= smart_poll(ups
->UPS_Cmd
[CI_BATTDAT
], ups
);
712 Dmsg1(80, "Got CI_BATTDAT: %s\n", answer
);
713 astrncpy(ups
->battdat
, answer
, sizeof(ups
->battdat
));
716 /* Nominal output voltage when on batteries */
717 if (ups
->UPS_Cap
[CI_NOMOUTV
]) {
718 answer
= smart_poll(ups
->UPS_Cmd
[CI_NOMOUTV
], ups
);
719 Dmsg1(80, "Got CI_NOMOUTV: %s\n", answer
);
720 ups
->NomOutputVoltage
= (int)atof(answer
);
723 /* Nominal battery voltage */
724 if (ups
->UPS_Cap
[CI_NOMBATTV
]) {
725 answer
= smart_poll(ups
->UPS_Cmd
[CI_NOMBATTV
], ups
);
726 Dmsg1(80, "Got CI_NOMBATTV: %s\n", answer
);
727 ups
->nombattv
= atof(answer
);
730 /* Firmware revision */
731 if (ups
->UPS_Cap
[CI_REVNO
]) {
732 answer
= smart_poll(ups
->UPS_Cmd
[CI_REVNO
], ups
);
733 Dmsg1(80, "Got CI_REVNO: %s\n", answer
);
734 astrncpy(ups
->firmrev
, answer
, sizeof(ups
->firmrev
));
737 /* Number of external batteries installed */
738 if (ups
->UPS_Cap
[CI_EXTBATTS
]) {
739 answer
= smart_poll(ups
->UPS_Cmd
[CI_EXTBATTS
], ups
);
740 Dmsg1(80, "Got CI_EXTBATTS: %s\n", answer
);
741 ups
->extbatts
= (int)atof(answer
);
744 /* Number of bad batteries installed */
745 if (ups
->UPS_Cap
[CI_BADBATTS
]) {
746 answer
= smart_poll(ups
->UPS_Cmd
[CI_BADBATTS
], ups
);
747 Dmsg1(80, "Got CI_BADBATTS: %s\n", answer
);
748 ups
->badbatts
= (int)atof(answer
);
752 if (ups
->UPS_Cap
[CI_UPSMODEL
]) {
753 answer
= smart_poll(ups
->UPS_Cmd
[CI_UPSMODEL
], ups
);
754 if (ups
->UPS_Cmd
[CI_UPSMODEL
] == APC_CMD_OLDFWREV
) {
755 /* Derive UPS model from old fw rev */
756 astrncpy(ups
->upsmodel
, get_model_from_oldfwrev(answer
),
757 sizeof(ups
->upsmodel
));
759 astrncpy(ups
->upsmodel
, answer
, sizeof(ups
->upsmodel
));
761 Dmsg1(80, "Got CI_UPSMODEL: %s\n", ups
->upsmodel
);
764 /* EPROM Capabilities */
765 if (ups
->UPS_Cap
[CI_EPROM
]) {
766 answer
= smart_poll(ups
->UPS_Cmd
[CI_EPROM
], ups
);
767 Dmsg1(80, "Got CI_EPROM: %s\n", answer
);
768 astrncpy(ups
->eprom
, answer
, sizeof(ups
->eprom
));
774 int apcsmart_ups_entry_point(UPSINFO
*ups
, int command
, void *data
)
776 int retries
= 5; /* Number of retries if reason is NA (see below) */
780 case DEVICE_CMD_SET_DUMB_MODE
:
781 /* Set dumb mode for a smart UPS */
782 write(ups
->fd
, "R", 1); /* enter dumb mode */
784 getline(ans
, sizeof(ans
), ups
);
785 printf("Going dumb: %s\n", ans
);
788 case DEVICE_CMD_GET_SELFTEST_MSG
:
789 /* Results of last self test */
790 if (ups
->UPS_Cap
[CI_ST_STAT
]) {
791 ups
->testresult
= decode_testresult(
792 smart_poll(ups
->UPS_Cmd
[CI_ST_STAT
], ups
));
796 case DEVICE_CMD_CHECK_SELFTEST
:
798 Dmsg0(80, "Checking self test.\n");
802 * One day we will do this test inside the driver and not as an
805 /* Reason for last transfer to batteries */
806 if (ups
->UPS_Cap
[CI_WHY_BATT
]) {
807 ups
->lastxfer
= XFER_NA
;
808 while (ups
->lastxfer
== XFER_NA
&& retries
--) {
809 ups
->lastxfer
= decode_lastxfer(
810 smart_poll(ups
->UPS_Cmd
[CI_WHY_BATT
], ups
));
811 if (ups
->lastxfer
== XFER_NA
) {
812 Dmsg0(80, "Transfer reason still not available.\n");
814 sleep(2); /* debounce */
817 * Be careful because if we go out of here without
818 * knowing the reason of transfer (i.e. the reason
819 * is "NA", apcupsd will think this is a power failure
820 * even if it is a self test. Not much of a problem
821 * as this should not happen.
822 * We allow 5 retries for reading reason from UPS before
825 } else if (ups
->lastxfer
== XFER_SELFTEST
) {
826 ups
->SelfTest
= time(NULL
);
827 Dmsg1(80, "Self Test time: %s", ctime(&ups
->SelfTest
));