UPS: apcupsd clean sources
[tomato.git] / release / src / router / apcupsd / src / drivers / apcsmart / smart.c
blobab94a1bdce3ad0c9dd5051db8963caf9138d2f84
2 /*
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
21 * All rights reserved
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
36 * 8.11.1995
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,
62 MA 02111-1307, USA.
67 #include "apc.h"
68 #include "apcsmart.h"
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)
77 * Responses are:
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')
84 return TEST_PASSED;
85 else if (str[0] == 'B' && str[1] == 'T')
86 return TEST_FAILCAP;
87 else if (str[0] == 'N' && str[1] == 'G')
88 return TEST_FAILLOAD;
90 return TEST_NONE;
93 /* Convert UPS response to enum and string */
94 static LastXferCause decode_lastxfer(char *str)
96 Dmsg1(80, "Transfer reason: %c\n", *str);
98 switch (*str) {
99 case 'N':
100 return XFER_NA;
101 case 'R':
102 return XFER_RIPPLE;
103 case 'H':
104 return XFER_OVERVOLT;
105 case 'L':
106 return XFER_UNDERVOLT;
107 case 'T':
108 return XFER_NOTCHSPIKE;
109 case 'O':
110 return XFER_NONE;
111 case 'K':
112 return XFER_FORCED;
113 case 'S':
114 return XFER_SELFTEST;
115 default:
116 return XFER_UNKNOWN;
120 int apc_enable(UPSINFO *ups)
122 /* Enable APC Smart UPS */
123 smart_poll('Y', ups);
124 smart_poll('Y', ups);
125 return 1;
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];
137 int stat, retry;
139 *answer = 0;
140 if (ups->fd == -1)
141 return answer;
143 /* Don't retry Y/SM command */
144 retry = (cmd == 'Y') ? 0 : 2;
146 do {
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--);
157 return answer;
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)
173 int i = 0;
174 int ending = 0;
175 char c;
176 int retval;
177 int wait;
179 if (s != NULL)
180 wait = TIMER_FAST; /* 1 sec, expect fast response */
181 else
182 wait = ups->wait_time;
184 #ifdef HAVE_MINGW
185 /* Set read() timeout since we have no select() support. */
187 COMMTIMEOUTS ct;
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);
196 #endif
198 while (!ending) {
199 #if !defined(HAVE_MINGW)
200 fd_set rfds;
201 struct timeval tv;
203 FD_ZERO(&rfds);
204 FD_SET(ups->fd, &rfds);
205 tv.tv_sec = wait;
206 tv.tv_usec = 0;
208 errno = 0;
209 retval = select((ups->fd) + 1, &rfds, NULL, NULL, &tv);
211 switch (retval) {
212 case 0: /* No chars available in TIMER seconds. */
213 return FAILURE;
214 case -1:
215 if (errno == EINTR || errno == EAGAIN) { /* assume SIGCHLD */
216 continue;
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));
221 break;
222 default:
223 break;
225 #endif
227 do {
228 retval = read(ups->fd, &c, 1);
229 } while (retval == -1 && (errno == EAGAIN || errno == EINTR));
230 if (retval == 0) {
231 return FAILURE;
234 switch (c) {
236 * Here we can be called in two ways:
238 * s == NULL
239 * The shm lock is not held so we must hold it here.
241 * s != NULL
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
247 * routine.
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 = '!' */
256 if (s == NULL)
257 write_lock(ups);
258 ups->clear_online();
259 Dmsg0(80, "Got UPS ON BATT.\n");
260 if (s == NULL) {
261 write_unlock(ups);
262 ending = 1;
264 break;
265 case UPS_REPLACE_BATTERY: /* UPS_REPLACE_BATTERY = '#' */
266 if (s == NULL)
267 write_lock(ups);
268 ups->set_replacebatt();
269 Dmsg0(80, "Got UPS REPLACE_BATT.\n");
270 if (s == NULL) {
271 write_unlock(ups);
272 ending = 1;
274 break;
275 case UPS_ON_LINE: /* UPS_ON_LINE = '$' */
276 if (s == NULL)
277 write_lock(ups);
278 ups->set_online();
279 Dmsg0(80, "Got UPS ON LINE.\n");
280 if (s == NULL) {
281 write_unlock(ups);
282 ending = 1;
284 break;
285 case BATT_LOW: /* BATT_LOW = '%' */
286 if (s == NULL)
287 write_lock(ups);
288 ups->set_battlow();
289 Dmsg0(80, "Got UPS BATT_LOW.\n");
290 if (s == NULL) {
291 write_unlock(ups);
292 ending = 1;
294 break;
295 case BATT_OK: /* BATT_OK = '+' */
296 if (s == NULL)
297 write_lock(ups);
298 ups->clear_battlow();
299 Dmsg0(80, "Got UPS BATT_OK.\n");
300 if (s == NULL) {
301 write_unlock(ups);
302 ending = 1;
304 break;
306 case UPS_EPROM_CHANGE: /* UPS_EPROM_CHANGE = '|' */
307 case UPS_TRAILOR: /* UPS_TRAILOR = ':' */
308 break;
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' */
315 if (s != NULL)
316 ending = 1; /* This what we waited for */
317 break;
318 case UPS_CR: /* UPS_CR = '\r' */
319 break;
320 default:
321 if (s != NULL) {
322 if (i + 1 < len)
323 s[i++] = c;
324 else
325 ending = 1; /* no more room in buffer */
327 break;
331 if (s != NULL) {
332 s[i] = '\0';
334 return SUCCESS;
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;
344 if (linkcheck)
345 return;
347 linkcheck = TRUE; /* prevent recursion */
349 tcflush(ups->fd, TCIOFLUSH);
350 if (strcmp(smart_poll('Y', ups), "SM") == 0) {
351 linkcheck = FALSE;
352 ups->clear_commlost();
353 return;
356 write_unlock(ups);
358 gettimeofday(&start, NULL);
359 prev = start;
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()) {
368 ups->set_commlost();
369 generate_event(ups, CMDCOMMFAILURE);
370 prev = now;
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);
377 prev = now;
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.
387 sleep(1);
388 tcflush(ups->fd, TCIOFLUSH);
391 write_lock(ups);
393 if (ups->is_commlost()) {
394 ups->clear_commlost();
395 generate_event(ups, CMDCOMMOK);
398 linkcheck = FALSE;
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)
409 time_t now;
410 char *answer;
413 * We need it for self-test start time.
415 now = time(NULL);
417 write_lock(ups);
419 UPSlinkCheck(ups); /* make sure serial port is working */
421 ups->poll_time = time(NULL); /* save time stamp */
423 /* UPS_STATUS */
424 if (ups->UPS_Cap[CI_STATUS]) {
425 char status[10];
426 int retries = 5; /* Number of retries on status read */
428 again:
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.
439 * XXX
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))
446 goto again;
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);
465 * XXX
467 * See if this is a self test rather than power failure
468 * But not now !
469 * When we will be ready we will copy the code below inside
470 * the driver entry point, for performing this check inside the
471 * driver.
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);
482 /* LINE_VOLTAGE */
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);
489 /* UPS_LINE_MAX */
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);
496 /* UPS_LINE_MIN */
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);
503 /* OUTPUT_VOLTAGE */
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);
517 /* BATT_VOLTAGE */
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);
524 /* UPS_LOAD */
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);
531 /* LINE_FREQ */
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);
545 /* UPS_TEMP */
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);
559 /* Register 1 */
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);
566 /* Register 2 */
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));
574 /* Register 3 */
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 */
604 write_unlock(ups);
606 return SUCCESS;
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
614 * UPS.
616 int apcsmart_ups_read_static_data(UPSINFO *ups)
618 char *answer;
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.
625 /* SENSITIVITY */
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));
632 /* WAKEUP_DELAY */
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);
639 /* SLEEP_DELAY */
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);
667 /* ALARM_STATUS */
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);
681 /* UPS_NAME */
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));
688 /* UPS_SELFTEST */
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);
751 /* UPS model */
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));
758 } else {
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));
771 return SUCCESS;
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) */
777 char ans[20];
779 switch (command) {
780 case DEVICE_CMD_SET_DUMB_MODE:
781 /* Set dumb mode for a smart UPS */
782 write(ups->fd, "R", 1); /* enter dumb mode */
783 *ans = 0;
784 getline(ans, sizeof(ans), ups);
785 printf("Going dumb: %s\n", ans);
786 break;
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));
794 break;
796 case DEVICE_CMD_CHECK_SELFTEST:
798 Dmsg0(80, "Checking self test.\n");
800 * XXX
802 * One day we will do this test inside the driver and not as an
803 * entry point.
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");
813 if (retries > 0)
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
823 * giving up.
825 } else if (ups->lastxfer == XFER_SELFTEST) {
826 ups->SelfTest = time(NULL);
827 Dmsg1(80, "Self Test time: %s", ctime(&ups->SelfTest));
831 break;
833 default:
834 return FAILURE;
835 break;
838 return SUCCESS;