4 * Do APC EEPROM changes.
8 * Copyright (C) 2000-2004 Kern Sibbald
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of version 2 of the GNU General
12 * Public License as published by the Free Software Foundation.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * General Public License for more details.
19 * You should have received a copy of the GNU General Public
20 * License along with this program; if not, write to the Free
21 * Software Foundation, Inc., 59 Temple Place - Suite 330, Boston,
28 static void change_ups_battery_date(UPSINFO
*ups
, const char *newdate
);
29 static void change_ups_name(UPSINFO
*ups
, const char *newname
);
30 static void change_extended(UPSINFO
*ups
);
31 static int change_ups_eeprom_item(UPSINFO
*ups
, const char *title
, const char cmd
,
35 /*********************************************************************/
36 int apcsmart_ups_program_eeprom(UPSINFO
*ups
, int command
, const char *data
)
41 apcsmart_ups_get_capabilities(ups
);
44 case CI_BATTDAT
: /* change battery date */
45 if (ups
->UPS_Cap
[CI_BATTDAT
]) {
46 printf("Attempting to update UPS battery date ...\n");
47 change_ups_battery_date(ups
, data
);
49 printf("UPS battery date configuration not supported by this UPS.\n");
55 if (ups
->UPS_Cap
[CI_IDEN
]) {
56 printf("Attempting to rename UPS ...\n");
57 change_ups_name(ups
, data
);
59 printf("UPS name configuration not supported by this UPS.\n");
66 if (ups
->UPS_Cap
[CI_SENS
]) {
67 asnprintf(setting
, sizeof(setting
), "%.1s", data
);
68 change_ups_eeprom_item(ups
, "sensitivity", ups
->UPS_Cmd
[CI_SENS
], setting
);
70 printf("UPS sensitivity configuration not supported by this UPS.\n");
77 if (ups
->UPS_Cap
[CI_DALARM
]) {
78 asnprintf(setting
, sizeof(setting
), "%.1s", data
);
79 change_ups_eeprom_item(ups
, "alarm status", ups
->UPS_Cmd
[CI_DALARM
],
82 printf("UPS alarm status configuration not supported by this UPS.\n");
87 /* LOWBATT_SHUTDOWN_LEVEL */
89 if (ups
->UPS_Cap
[CI_DLBATT
]) {
90 asnprintf(setting
, sizeof(setting
), "%02d", (int)atoi(data
));
91 change_ups_eeprom_item(ups
, "low battery warning delay",
92 ups
->UPS_Cmd
[CI_DLBATT
], setting
);
95 "UPS low battery warning configuration not supported by this UPS.\n");
102 if (ups
->UPS_Cap
[CI_DWAKE
]) {
103 asnprintf(setting
, sizeof(setting
), "%03d", (int)atoi(data
));
104 change_ups_eeprom_item(ups
, "wakeup delay", ups
->UPS_Cmd
[CI_DWAKE
],
107 printf("UPS wakeup delay configuration not supported by this UPS.\n");
115 if (ups
->UPS_Cap
[CI_DSHUTD
]) {
116 asnprintf(setting
, sizeof(setting
), "%03d", (int)atoi(data
));
117 change_ups_eeprom_item(ups
, "shutdown delay", ups
->UPS_Cmd
[CI_DSHUTD
],
120 printf("UPS shutdown delay configuration not supported by this UPS.\n");
125 /* LOW_TRANSFER_LEVEL */
127 if (ups
->UPS_Cap
[CI_LTRANS
]) {
128 asnprintf(setting
, sizeof(setting
), "%03d", (int)atoi(data
));
129 change_ups_eeprom_item(ups
, "lower transfer voltage",
130 ups
->UPS_Cmd
[CI_LTRANS
], setting
);
133 "UPS low transfer voltage configuration not supported by this UPS.\n");
138 /* HIGH_TRANSFER_LEVEL */
140 if (ups
->UPS_Cap
[CI_HTRANS
]) {
141 asnprintf(setting
, sizeof(setting
), "%03d", (int)atoi(data
));
142 change_ups_eeprom_item(ups
, "high transfer voltage",
143 ups
->UPS_Cmd
[CI_HTRANS
], setting
);
146 "UPS high transfer voltage configuration not supported by this UPS.\n");
151 /* UPS_BATT_CAP_RETURN */
153 if (ups
->UPS_Cap
[CI_RETPCT
]) {
154 asnprintf(setting
, sizeof(setting
), "%02d", (int)atoi(data
));
155 change_ups_eeprom_item(ups
, "return threshold percent",
156 ups
->UPS_Cmd
[CI_RETPCT
], setting
);
159 "UPS return threshold configuration not supported by this UPS.\n");
166 if (ups
->UPS_Cap
[CI_STESTI
]) {
167 asnprintf(setting
, sizeof(setting
), "%.3s", data
);
168 /* Make sure "ON" is 3 characters */
169 if (setting
[2] == 0) {
173 change_ups_eeprom_item(ups
, "self test interval", ups
->UPS_Cmd
[CI_STESTI
],
177 "UPS self test interval configuration not supported by this UPS.\n");
184 if (ups
->UPS_Cap
[CI_NOMOUTV
]) {
185 asnprintf(setting
, sizeof(setting
), "%03d", (int)atoi(data
));
186 change_ups_eeprom_item(ups
, "output voltage on batteries",
187 ups
->UPS_Cmd
[CI_NOMOUTV
], setting
);
190 "UPS output voltage on batteries configuration not supported by this UPS.\n");
196 case -1: /* old style from .conf file */
198 printf("Attempting to configure UPS ...\n");
199 change_extended(ups
); /* set new values in UPS */
201 printf("\nReading updated UPS configuration ...\n\n");
202 device_read_volatile_data(ups
);
203 device_read_static_data(ups
);
205 /* Print report of status */
206 output_status(ups
, 0, stat_open
, stat_print
, stat_close
);
210 printf("Ignoring unknown config request command=%d\n", command
);
218 /*********************************************************************/
219 static void change_ups_name(UPSINFO
*ups
, const char *newname
)
224 char a
= ups
->UPS_Cmd
[CI_CYCLE_EPROM
];
225 char c
= ups
->UPS_Cmd
[CI_IDEN
];
227 int j
= strlen(newname
);
232 fprintf(stderr
, "Error, new name of zero length.\n");
235 j
= 8; /* maximum size */
238 strncpy(name
, newname
, 9);
240 /* blank fill to 8 chars */
247 write(ups
->fd
, &c
, 1); /* c = 'c' */
248 getline(response
, sizeof(response
), ups
);
249 fprintf(stderr
, "The old UPS name is: %s\n", response
);
251 /* Tell UPS we will change name */
252 write(ups
->fd
, &a
, 1); /* a = '-' */
256 for (i
= 0; i
< 8; i
++) {
257 write(ups
->fd
, n
++, 1);
261 /* Expect OK after successful name change */
263 getline(response
, sizeof(response
), ups
);
264 if (strcmp(response
, "OK") != 0) {
265 fprintf(stderr
, "\nError changing UPS name\n");
268 ups
->upsname
[0] = '\0';
269 smart_poll(ups
->UPS_Cmd
[CI_IDEN
], ups
);
270 astrncpy(ups
->upsname
, smart_poll(ups
->UPS_Cmd
[CI_IDEN
], ups
),
271 sizeof(ups
->upsname
));
273 fprintf(stderr
, "The new UPS name is: %s\n", ups
->upsname
);
277 * Update date battery replaced
279 static void change_ups_battery_date(UPSINFO
*ups
, const char *newdate
)
284 char a
= ups
->UPS_Cmd
[CI_CYCLE_EPROM
];
285 char c
= ups
->UPS_Cmd
[CI_BATTDAT
];
287 int j
= strlen(newdate
);
292 fprintf(stderr
, "Error, new battery date must be 8 characters long.\n");
296 astrncpy(battdat
, newdate
, sizeof(battdat
));
298 /* Ask for battdat */
299 write(ups
->fd
, &c
, 1); /* c = 'x' */
300 getline(response
, sizeof(response
), ups
);
301 fprintf(stderr
, "The old UPS battery date is: %s\n", response
);
303 /* Tell UPS we will change battdat */
304 write(ups
->fd
, &a
, 1); /* a = '-' */
308 for (i
= 0; i
< 8; i
++) {
309 write(ups
->fd
, n
++, 1);
313 /* Expect OK after successful battdat change */
315 getline(response
, sizeof(response
), ups
);
316 if (strcmp(response
, "OK") != 0) {
317 fprintf(stderr
, "\nError changing UPS battery date\n");
320 ups
->battdat
[0] = '\0';
321 smart_poll(ups
->UPS_Cmd
[CI_BATTDAT
], ups
);
322 astrncpy(ups
->battdat
, smart_poll(ups
->UPS_Cmd
[CI_BATTDAT
], ups
),
323 sizeof(ups
->battdat
));
325 fprintf(stderr
, "The new UPS battery date is: %s\n", ups
->battdat
);
328 /*********************************************************************/
329 static int change_ups_eeprom_item(UPSINFO
*ups
, const char *title
, const char cmd
,
337 char a
= ups
->UPS_Cmd
[CI_CYCLE_EPROM
];
340 /* Ask for old value */
341 write(ups
->fd
, &cmd
, 1);
342 if (getline(oldvalue
, sizeof(oldvalue
), ups
) == FAILURE
) {
343 fprintf(stderr
, "Could not get old value of %s.\n", title
);
347 if (strcmp(oldvalue
, setting
) == 0) {
348 fprintf(stderr
, "The UPS %s remains unchanged as: %s\n", title
, oldvalue
);
352 fprintf(stderr
, "The old UPS %s is: %s\n", title
, oldvalue
);
353 astrncpy(allvalues
, oldvalue
, sizeof(allvalues
));
354 astrncat(allvalues
, " ", sizeof(allvalues
));
355 astrncpy(lastvalue
, oldvalue
, sizeof(lastvalue
));
357 /* Try a second time to ensure that it is a stable value */
358 write(ups
->fd
, &cmd
, 1);
360 getline(response
, sizeof(response
), ups
);
361 if (strcmp(oldvalue
, response
) != 0) {
362 fprintf(stderr
, "\nEEPROM value of %s is not stable\n", title
);
367 * Just before entering this loop, the last command sent
368 * to the UPS MUST be to query the old value.
370 for (i
= 0; i
< 10; i
++) {
371 write(ups
->fd
, &cmd
, 1);
372 getline(response1
, sizeof(response1
), ups
);
374 /* Tell UPS to cycle to next value */
375 write(ups
->fd
, &a
, 1); /* a = '-' */
377 /* Expect OK after successful change */
379 getline(response
, sizeof(response
), ups
);
380 if (strcmp(response
, "OK") != 0) {
381 fprintf(stderr
, "\nError changing UPS %s\n", title
);
382 fprintf(stderr
, "Got %s instead of OK\n\n", response
);
387 /* get cycled value */
388 write(ups
->fd
, &cmd
, 1);
389 getline(response1
, sizeof(response1
), ups
);
391 /* get cycled value again */
392 write(ups
->fd
, &cmd
, 1);
393 if (getline(response
, sizeof(response
), ups
) == FAILURE
||
394 strcmp(response1
, response
) != 0) {
395 fprintf(stderr
, "Error cycling values.\n");
396 getline(response
, sizeof(response
), ups
); /* eat any garbage */
399 if (strcmp(setting
, response
) == 0) {
400 fprintf(stderr
, "The new UPS %s is: %s\n", title
, response
);
401 sleep(10); /* allow things to settle down */
406 * Check if we cycled back to the same value, but permit
407 * a duplicate because the L for sensitivy appears
408 * twice in a row, i.e. H M L L.
410 if (strcmp(oldvalue
, response
) == 0 && i
> 0)
412 if (strcmp(lastvalue
, response
) != 0) {
413 astrncat(allvalues
, response
, sizeof(allvalues
));
414 astrncat(allvalues
, " ", sizeof(allvalues
));
415 astrncpy(lastvalue
, response
, sizeof(lastvalue
));
417 sleep(5); /* don't cycle too fast */
420 fprintf(stderr
, "Unable to change %s to: %s\n", title
, setting
);
421 fprintf(stderr
, "Permitted values are: %s\n", allvalues
);
422 getline(response
, sizeof(response
), ups
); /* eat any garbage */
429 * Set new values in EEPROM memmory. Change the UPS EEPROM.
431 static void change_extended(UPSINFO
*ups
)
435 apcsmart_ups_get_capabilities(ups
);
438 * Note, a value of -1 in the variable at the beginning
439 * means that the user did not put a configuration directive
440 * in /etc/apcupsd/apcupsd.conf. Consequently, if no value
441 * was given, we won't attept to change it.
445 if (ups
->UPS_Cap
[CI_SENS
] && strcmp(ups
->sensitivity
, "-1") != 0) {
446 asnprintf(setting
, sizeof(setting
), "%.1s", ups
->sensitivity
);
447 change_ups_eeprom_item(ups
, "sensitivity", ups
->UPS_Cmd
[CI_SENS
], setting
);
451 if (ups
->UPS_Cap
[CI_DWAKE
] && ups
->dwake
!= -1) {
452 asnprintf(setting
, sizeof(setting
), "%03d", (int)ups
->dwake
);
453 change_ups_eeprom_item(ups
, "wakeup delay", ups
->UPS_Cmd
[CI_DWAKE
], setting
);
457 if (ups
->UPS_Cap
[CI_DSHUTD
] && ups
->dshutd
!= -1) {
458 asnprintf(setting
, sizeof(setting
), "%03d", (int)ups
->dshutd
);
459 change_ups_eeprom_item(ups
, "shutdown delay", ups
->UPS_Cmd
[CI_DSHUTD
],
463 /* LOW_TRANSFER_LEVEL */
464 if (ups
->UPS_Cap
[CI_LTRANS
] && ups
->lotrans
!= -1) {
465 asnprintf(setting
, sizeof(setting
), "%03d", (int)ups
->lotrans
);
466 change_ups_eeprom_item(ups
, "lower transfer voltage",
467 ups
->UPS_Cmd
[CI_LTRANS
], setting
);
470 /* HIGH_TRANSFER_LEVEL */
471 if (ups
->UPS_Cap
[CI_HTRANS
] && ups
->hitrans
!= -1) {
472 asnprintf(setting
, sizeof(setting
), "%03d", (int)ups
->hitrans
);
473 change_ups_eeprom_item(ups
, "upper transfer voltage",
474 ups
->UPS_Cmd
[CI_HTRANS
], setting
);
477 /* UPS_BATT_CAP_RETURN */
478 if (ups
->UPS_Cap
[CI_RETPCT
] && ups
->rtnpct
!= -1) {
479 asnprintf(setting
, sizeof(setting
), "%02d", (int)ups
->rtnpct
);
480 change_ups_eeprom_item(ups
, "return threshold percent",
481 ups
->UPS_Cmd
[CI_RETPCT
], setting
);
485 if (ups
->UPS_Cap
[CI_DALARM
] && strcmp(ups
->beepstate
, "-1") != 0) {
486 asnprintf(setting
, sizeof(setting
), "%.1s", ups
->beepstate
);
487 change_ups_eeprom_item(ups
, "alarm delay", ups
->UPS_Cmd
[CI_DALARM
], setting
);
490 /* LOWBATT_SHUTDOWN_LEVEL */
491 if (ups
->UPS_Cap
[CI_DLBATT
] && ups
->dlowbatt
!= -1) {
492 asnprintf(setting
, sizeof(setting
), "%02d", (int)ups
->dlowbatt
);
493 change_ups_eeprom_item(ups
, "low battery warning delay",
494 ups
->UPS_Cmd
[CI_DLBATT
], setting
);
498 if (ups
->UPS_Cap
[CI_STESTI
] && strcmp(ups
->selftest
, "-1") != 0) {
499 asnprintf(setting
, sizeof(setting
), "%.3s", ups
->selftest
);
500 /* Make sure "ON" is 3 characters */
501 if (setting
[2] == 0) {
505 change_ups_eeprom_item(
506 ups
, "self test interval", ups
->UPS_Cmd
[CI_STESTI
], setting
);
510 if (ups
->UPS_Cap
[CI_NOMOUTV
] && ups
->NomOutputVoltage
!= -1) {
511 asnprintf(setting
, sizeof(setting
), "%03d", (int)ups
->NomOutputVoltage
);
512 change_ups_eeprom_item(ups
, "output voltage on batteries",
513 ups
->UPS_Cmd
[CI_NOMOUTV
], setting
);