1 /*
2 * apceeprom.c
3 *
4 * Do APC EEPROM changes.
5 */
6
7 /*
8 * Copyright (C) 2000-2004 Kern Sibbald
9 *
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.
13 *
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.
18 *
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., 51 Franklin Street, Fifth Floor, Boston,
22 * MA 02110-1335, USA.
23 */
24
25 #include "apc.h"
26 #include "apcsmart.h"
27
28
29 /*********************************************************************/
program_eeprom(int command,const char * data)30 bool ApcSmartUpsDriver::program_eeprom(int command, const char *data)
31 {
32 char setting[20];
33
34 setup();
35 get_capabilities();
36
37 switch (command) {
38 case CI_BATTDAT: /* change battery date */
39 if (_ups->UPS_Cap[CI_BATTDAT]) {
40 printf("Attempting to update UPS battery date ...\n");
41 change_ups_battery_date(data);
42 } else {
43 printf("UPS battery date configuration not supported by this UPS.\n");
44 return 0;
45 }
46 break;
47
48 case CI_IDEN:
49 if (_ups->UPS_Cap[CI_IDEN]) {
50 printf("Attempting to rename UPS ...\n");
51 change_ups_name(data);
52 } else {
53 printf("UPS name configuration not supported by this UPS.\n");
54 return 0;
55 }
56 break;
57
58 /* SENSITIVITY */
59 case CI_SENS:
60 if (_ups->UPS_Cap[CI_SENS]) {
61 asnprintf(setting, sizeof(setting), "%.1s", data);
62 change_ups_eeprom_item("sensitivity", _ups->UPS_Cmd[CI_SENS], setting);
63 } else {
64 printf("UPS sensitivity configuration not supported by this UPS.\n");
65 return 0;
66 }
67 break;
68
69 /* ALARM_STATUS */
70 case CI_DALARM:
71 if (_ups->UPS_Cap[CI_DALARM]) {
72 asnprintf(setting, sizeof(setting), "%.1s", data);
73 change_ups_eeprom_item("alarm status", _ups->UPS_Cmd[CI_DALARM],
74 setting);
75 } else {
76 printf("UPS alarm status configuration not supported by this UPS.\n");
77 return 0;
78 }
79 break;
80
81 /* LOWBATT_SHUTDOWN_LEVEL */
82 case CI_DLBATT:
83 if (_ups->UPS_Cap[CI_DLBATT]) {
84 asnprintf(setting, sizeof(setting), "%02d", (int)atoi(data));
85 change_ups_eeprom_item("low battery warning delay",
86 _ups->UPS_Cmd[CI_DLBATT], setting);
87 } else {
88 printf(
89 "UPS low battery warning configuration not supported by this UPS.\n");
90 return 0;
91 }
92 break;
93
94 /* WAKEUP_DELAY */
95 case CI_DWAKE:
96 if (_ups->UPS_Cap[CI_DWAKE]) {
97 asnprintf(setting, sizeof(setting), "%03d", (int)atoi(data));
98 change_ups_eeprom_item("wakeup delay", _ups->UPS_Cmd[CI_DWAKE],
99 setting);
100 } else {
101 printf("UPS wakeup delay configuration not supported by this UPS.\n");
102 return 0;
103 }
104 break;
105
106
107 /* SLEEP_DELAY */
108 case CI_DSHUTD:
109 if (_ups->UPS_Cap[CI_DSHUTD]) {
110 asnprintf(setting, sizeof(setting), "%03d", (int)atoi(data));
111 change_ups_eeprom_item("shutdown delay", _ups->UPS_Cmd[CI_DSHUTD],
112 setting);
113 } else {
114 printf("UPS shutdown delay configuration not supported by this UPS.\n");
115 return 0;
116 }
117 break;
118
119 /* LOW_TRANSFER_LEVEL */
120 case CI_LTRANS:
121 if (_ups->UPS_Cap[CI_LTRANS]) {
122 asnprintf(setting, sizeof(setting), "%03d", (int)atoi(data));
123 change_ups_eeprom_item("lower transfer voltage",
124 _ups->UPS_Cmd[CI_LTRANS], setting);
125 } else {
126 printf(
127 "UPS low transfer voltage configuration not supported by this UPS.\n");
128 return 0;
129 }
130 break;
131
132 /* HIGH_TRANSFER_LEVEL */
133 case CI_HTRANS:
134 if (_ups->UPS_Cap[CI_HTRANS]) {
135 asnprintf(setting, sizeof(setting), "%03d", (int)atoi(data));
136 change_ups_eeprom_item("high transfer voltage",
137 _ups->UPS_Cmd[CI_HTRANS], setting);
138 } else {
139 printf(
140 "UPS high transfer voltage configuration not supported by this UPS.\n");
141 return 0;
142 }
143 break;
144
145 /* UPS_BATT_CAP_RETURN */
146 case CI_RETPCT:
147 if (_ups->UPS_Cap[CI_RETPCT]) {
148 asnprintf(setting, sizeof(setting), "%02d", (int)atoi(data));
149 change_ups_eeprom_item("return threshold percent",
150 _ups->UPS_Cmd[CI_RETPCT], setting);
151 } else {
152 printf(
153 "UPS return threshold configuration not supported by this UPS.\n");
154 return 0;
155 }
156 break;
157
158 /* UPS_SELFTEST */
159 case CI_STESTI:
160 if (_ups->UPS_Cap[CI_STESTI]) {
161 asnprintf(setting, sizeof(setting), "%.3s", data);
162 /* Make sure "ON" is 3 characters */
163 if (setting[2] == 0) {
164 setting[2] = ' ';
165 setting[3] = 0;
166 }
167 change_ups_eeprom_item("self test interval", _ups->UPS_Cmd[CI_STESTI],
168 setting);
169 } else {
170 printf(
171 "UPS self test interval configuration not supported by this UPS.\n");
172 return 0;
173 }
174 break;
175
176 /* OUTPUT_VOLTAGE */
177 case CI_NOMOUTV:
178 if (_ups->UPS_Cap[CI_NOMOUTV]) {
179 asnprintf(setting, sizeof(setting), "%03d", (int)atoi(data));
180 change_ups_eeprom_item("output voltage on batteries",
181 _ups->UPS_Cmd[CI_NOMOUTV], setting);
182 } else {
183 printf(
184 "UPS output voltage on batteries configuration not supported by this UPS.\n");
185 return 0;
186 }
187 break;
188
189
190 case -1: /* old style from .conf file */
191
192 printf("Attempting to configure UPS ...\n");
193 change_extended(); /* set new values in UPS */
194
195 printf("\nReading updated UPS configuration ...\n\n");
196 read_volatile_data();
197 read_static_data();
198
199 /* Print report of status */
200 output_status(_ups, 0, stat_open, stat_print, stat_close);
201 break;
202
203 default:
204 printf("Ignoring unknown config request command=%d\n", command);
205 return 0;
206 break;
207 }
208
209 return 1;
210 }
211
212 /*********************************************************************/
change_ups_name(const char * newname)213 void ApcSmartUpsDriver::change_ups_name(const char *newname)
214 {
215 char *n;
216 char response[32];
217 char name[10];
218 char a = _ups->UPS_Cmd[CI_CYCLE_EPROM];
219 char c = _ups->UPS_Cmd[CI_IDEN];
220 int i;
221 int j = strlen(newname);
222
223 name[0] = '\0';
224
225 if (j == 0) {
226 fprintf(stderr, "Error, new name of zero length.\n");
227 return;
228 } else if (j > 8) {
229 j = 8; /* maximum size */
230 }
231
232 strncpy(name, newname, 9);
233
234 /* blank fill to 8 chars */
235 while (j < 8) {
236 name[j] = ' ';
237 j++;
238 }
239
240 /* Ask for name */
241 write(_ups->fd, &c, 1); /* c = 'c' */
242 getline(response, sizeof(response));
243 fprintf(stderr, "The old UPS name is: %s\n", response);
244
245 /* Tell UPS we will change name */
246 write(_ups->fd, &a, 1); /* a = '-' */
247 sleep(1);
248
249 n = name;
250 for (i = 0; i < 8; i++) {
251 write(_ups->fd, n++, 1);
252 sleep(1);
253 }
254
255 /* Expect OK after successful name change */
256 *response = 0;
257 getline(response, sizeof(response));
258 if (strcmp(response, "OK") != 0) {
259 fprintf(stderr, "\nError changing UPS name\n");
260 }
261
262 _ups->upsname[0] = '\0';
263 smart_poll(_ups->UPS_Cmd[CI_IDEN]);
264 strlcpy(_ups->upsname, smart_poll(_ups->UPS_Cmd[CI_IDEN]),
265 sizeof(_ups->upsname));
266
267 fprintf(stderr, "The new UPS name is: %s\n", _ups->upsname);
268 }
269
270 /*
271 * Update date battery replaced
272 */
change_ups_battery_date(const char * newdate)273 void ApcSmartUpsDriver::change_ups_battery_date(const char *newdate)
274 {
275 char *n;
276 char response[32];
277 char battdat[9];
278 char a = _ups->UPS_Cmd[CI_CYCLE_EPROM];
279 char c = _ups->UPS_Cmd[CI_BATTDAT];
280 int i;
281 int j = strlen(newdate);
282
283 battdat[0] = '\0';
284
285 if (j != 8) {
286 fprintf(stderr, "Error, new battery date must be 8 characters long.\n");
287 return;
288 }
289
290 strlcpy(battdat, newdate, sizeof(battdat));
291
292 /* Ask for battdat */
293 write(_ups->fd, &c, 1); /* c = 'x' */
294 getline(response, sizeof(response));
295 fprintf(stderr, "The old UPS battery date is: %s\n", response);
296
297 /* Tell UPS we will change battdat */
298 write(_ups->fd, &a, 1); /* a = '-' */
299 sleep(1);
300
301 n = battdat;
302 for (i = 0; i < 8; i++) {
303 write(_ups->fd, n++, 1);
304 sleep(1);
305 }
306
307 /* Expect OK after successful battdat change */
308 *response = 0;
309 getline(response, sizeof(response));
310 if (strcmp(response, "OK") != 0) {
311 fprintf(stderr, "\nError changing UPS battery date\n");
312 }
313
314 _ups->battdat[0] = '\0';
315 smart_poll(_ups->UPS_Cmd[CI_BATTDAT]);
316 strlcpy(_ups->battdat, smart_poll(_ups->UPS_Cmd[CI_BATTDAT]),
317 sizeof(_ups->battdat));
318
319 fprintf(stderr, "The new UPS battery date is: %s\n", _ups->battdat);
320 }
321
322 /*********************************************************************/
change_ups_eeprom_item(const char * title,const char cmd,const char * setting)323 int ApcSmartUpsDriver::change_ups_eeprom_item(const char *title, const char cmd,
324 const char *setting)
325 {
326 char response[32];
327 char response1[32];
328 char oldvalue[32];
329 char lastvalue[32];
330 char allvalues[256];
331 char a = _ups->UPS_Cmd[CI_CYCLE_EPROM];
332 int i;
333
334 /* Ask for old value */
335 write(_ups->fd, &cmd, 1);
336 if (getline(oldvalue, sizeof(oldvalue)) == FAILURE) {
337 fprintf(stderr, "Could not get old value of %s.\n", title);
338 return FAILURE;
339 }
340
341 if (strcmp(oldvalue, setting) == 0) {
342 fprintf(stderr, "The UPS %s remains unchanged as: %s\n", title, oldvalue);
343 return SUCCESS;
344 }
345
346 fprintf(stderr, "The old UPS %s is: %s\n", title, oldvalue);
347 strlcpy(allvalues, oldvalue, sizeof(allvalues));
348 strlcat(allvalues, " ", sizeof(allvalues));
349 strlcpy(lastvalue, oldvalue, sizeof(lastvalue));
350
351 /* Try a second time to ensure that it is a stable value */
352 write(_ups->fd, &cmd, 1);
353 *response = 0;
354 getline(response, sizeof(response));
355 if (strcmp(oldvalue, response) != 0) {
356 fprintf(stderr, "\nEEPROM value of %s is not stable\n", title);
357 return FAILURE;
358 }
359
360 /*
361 * Just before entering this loop, the last command sent
362 * to the UPS MUST be to query the old value.
363 */
364 for (i = 0; i < 10; i++) {
365 write(_ups->fd, &cmd, 1);
366 getline(response1, sizeof(response1));
367
368 /* Tell UPS to cycle to next value */
369 write(_ups->fd, &a, 1); /* a = '-' */
370
371 /* Expect OK after successful change */
372 *response = 0;
373 getline(response, sizeof(response));
374 if (strcmp(response, "OK") != 0) {
375 fprintf(stderr, "\nError changing UPS %s\n", title);
376 fprintf(stderr, "Got %s instead of OK\n\n", response);
377 sleep(10);
378 return FAILURE;
379 }
380
381 /* get cycled value */
382 write(_ups->fd, &cmd, 1);
383 getline(response1, sizeof(response1));
384
385 /* get cycled value again */
386 write(_ups->fd, &cmd, 1);
387 if (getline(response, sizeof(response)) == FAILURE ||
388 strcmp(response1, response) != 0) {
389 fprintf(stderr, "Error cycling values.\n");
390 getline(response, sizeof(response)); /* eat any garbage */
391 return FAILURE;
392 }
393 if (strcmp(setting, response) == 0) {
394 fprintf(stderr, "The new UPS %s is: %s\n", title, response);
395 sleep(10); /* allow things to settle down */
396 return SUCCESS;
397 }
398
399 /*
400 * Check if we cycled back to the same value, but permit
401 * a duplicate because the L for sensitivy appears
402 * twice in a row, i.e. H M L L.
403 */
404 if (strcmp(oldvalue, response) == 0 && i > 0)
405 break;
406 if (strcmp(lastvalue, response) != 0) {
407 strlcat(allvalues, response, sizeof(allvalues));
408 strlcat(allvalues, " ", sizeof(allvalues));
409 strlcpy(lastvalue, response, sizeof(lastvalue));
410 }
411 sleep(5); /* don't cycle too fast */
412 }
413
414 fprintf(stderr, "Unable to change %s to: %s\n", title, setting);
415 fprintf(stderr, "Permitted values are: %s\n", allvalues);
416 getline(response, sizeof(response)); /* eat any garbage */
417
418 return FAILURE;
419 }
420
421
422 /*
423 * Set new values in EEPROM memmory. Change the UPS EEPROM.
424 */
change_extended()425 void ApcSmartUpsDriver::change_extended()
426 {
427 char setting[20];
428
429 get_capabilities();
430
431 /*
432 * Note, a value of -1 in the variable at the beginning
433 * means that the user did not put a configuration directive
434 * in /etc/apcctrl/apcctrl.conf. Consequently, if no value
435 * was given, we won't attept to change it.
436 */
437
438 /* SENSITIVITY */
439 if (_ups->UPS_Cap[CI_SENS] && strcmp(_ups->sensitivity, "-1") != 0) {
440 asnprintf(setting, sizeof(setting), "%.1s", _ups->sensitivity);
441 change_ups_eeprom_item("sensitivity", _ups->UPS_Cmd[CI_SENS], setting);
442 }
443
444 /* WAKEUP_DELAY */
445 if (_ups->UPS_Cap[CI_DWAKE] && _ups->dwake != -1) {
446 asnprintf(setting, sizeof(setting), "%03d", (int)_ups->dwake);
447 change_ups_eeprom_item("wakeup delay", _ups->UPS_Cmd[CI_DWAKE], setting);
448 }
449
450 /* SLEEP_DELAY */
451 if (_ups->UPS_Cap[CI_DSHUTD] && _ups->dshutd != -1) {
452 asnprintf(setting, sizeof(setting), "%03d", (int)_ups->dshutd);
453 change_ups_eeprom_item("shutdown delay", _ups->UPS_Cmd[CI_DSHUTD],
454 setting);
455 }
456
457 /* LOW_TRANSFER_LEVEL */
458 if (_ups->UPS_Cap[CI_LTRANS] && _ups->lotrans != -1) {
459 asnprintf(setting, sizeof(setting), "%03d", (int)_ups->lotrans);
460 change_ups_eeprom_item("lower transfer voltage",
461 _ups->UPS_Cmd[CI_LTRANS], setting);
462 }
463
464 /* HIGH_TRANSFER_LEVEL */
465 if (_ups->UPS_Cap[CI_HTRANS] && _ups->hitrans != -1) {
466 asnprintf(setting, sizeof(setting), "%03d", (int)_ups->hitrans);
467 change_ups_eeprom_item("upper transfer voltage",
468 _ups->UPS_Cmd[CI_HTRANS], setting);
469 }
470
471 /* UPS_BATT_CAP_RETURN */
472 if (_ups->UPS_Cap[CI_RETPCT] && _ups->rtnpct != -1) {
473 asnprintf(setting, sizeof(setting), "%02d", (int)_ups->rtnpct);
474 change_ups_eeprom_item("return threshold percent",
475 _ups->UPS_Cmd[CI_RETPCT], setting);
476 }
477
478 /* ALARM_STATUS */
479 if (_ups->UPS_Cap[CI_DALARM] && strcmp(_ups->beepstate, "-1") != 0) {
480 asnprintf(setting, sizeof(setting), "%.1s", _ups->beepstate);
481 change_ups_eeprom_item("alarm delay", _ups->UPS_Cmd[CI_DALARM], setting);
482 }
483
484 /* LOWBATT_SHUTDOWN_LEVEL */
485 if (_ups->UPS_Cap[CI_DLBATT] && _ups->dlowbatt != -1) {
486 asnprintf(setting, sizeof(setting), "%02d", (int)_ups->dlowbatt);
487 change_ups_eeprom_item("low battery warning delay",
488 _ups->UPS_Cmd[CI_DLBATT], setting);
489 }
490
491 /* UPS_SELFTEST */
492 if (_ups->UPS_Cap[CI_STESTI] && strcmp(_ups->selftest, "-1") != 0) {
493 asnprintf(setting, sizeof(setting), "%.3s", _ups->selftest);
494 /* Make sure "ON" is 3 characters */
495 if (setting[2] == 0) {
496 setting[2] = ' ';
497 setting[3] = 0;
498 }
499 change_ups_eeprom_item(
500 "self test interval", _ups->UPS_Cmd[CI_STESTI], setting);
501 }
502
503 /* OUTPUT_VOLTAGE */
504 if (_ups->UPS_Cap[CI_NOMOUTV] && _ups->NomOutputVoltage != -1) {
505 asnprintf(setting, sizeof(setting), "%03d", (int)_ups->NomOutputVoltage);
506 change_ups_eeprom_item("output voltage on batteries",
507 _ups->UPS_Cmd[CI_NOMOUTV], setting);
508 }
509 }
510