2
* sec.c -- new style SEC UPS Protocol driver
4
* Copyright (C) 2001 John Marley
5
* The author can be contacted at: John.Marley@alcatel.com.au
7
* This program is free software; you can redistribute it and/or modify
8
* it under the terms of the GNU General Public License as published by
9
* the Free Software Foundation; either version 2 of the License, or
10
* (at your option) any later version.
12
* This program is distributed in the hope that it will be useful,
13
* but WITHOUT ANY WARRANTY; without even the implied warranty of
14
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15
* GNU General Public License for more details.
17
* You should have received a copy of the GNU General Public License
18
* along with this program; if not, write to the Free Software
19
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21
* Author : John Marley
22
* Created On : Wed Mar 28 14:48:26 2001
23
* Last Modified By: John Marley
24
* Last Modified On: Tue May 8 13:03:42 2001
26
* Status : Unknown, Use with caution!
29
* Revision 1.2 2001/05/08 03:05:21 marleyj
30
* Added synthetic variables INFO_STATUS, INFO_ACFREQ, INFO_UTILITY,
31
* INFO_CURRENT, INFO_LOADPCT, INFO_LOADPWR and INFO_OUTVOLT.
33
* Revision 1.1 2001/05/02 04:54:19 marleyj
38
#define SEC_DRIVER_VERSION "$Revision: 1.2 $"
43
extern const char *device_path;
44
extern char upssend_endchar;
48
/* #define ENDCHAR '\r' */
49
/* #define IGNCHARS "" */
54
* Attempts to read up to count chars from the UPS in the buffer buf.
56
* Returns count or -1 if there was an error.
58
int upsread(char *buf, int count)
65
ret = read(upsfd, &in, 1);
66
if (ret < 1) return (-1);
78
* Read a SEC format message (^<cmdchar><len><data>) from the UPS
81
* Return -2 if failed to read valid response
82
* Return -1 if command failed (^0)
83
* Return 0 if command succeeded (^1)
84
* Return len if data returned
86
* Need to read one char at a time...
88
int sec_upsrecv (char *buf)
90
char recvbuf[512], lenbuf[4], in;
97
sa.sa_handler = timeout;
98
sigemptyset (&sigmask);
101
sigaction (SIGALRM, &sa, NULL);
105
upsdebugx(1, "sec_upsrecv...");
107
/* look for the startchar */
109
ret = read (upsfd, &in, 1);
111
if (in == SEC_MSG_STARTCHAR) break;
115
signal (SIGALRM, SIG_IGN);
116
upsdebugx(1, " FAILED to find start char %c",SEC_MSG_STARTCHAR);
122
/* we found a msg, which one? */
123
upsdebugx(1, " found start char...");
124
ret = read (upsfd, &in, 1);
126
if (in == SEC_DATAMSG) {
127
upsdebugx(1, " got a %c!",SEC_DATAMSG);
128
/* data being returned - get the length */
129
ret = upsread(lenbuf, 3);
132
signal (SIGALRM, SIG_IGN);
133
upsdebugx(1, " OOPS only %d of 3 chars read (%c)",ret,*lenbuf);
137
upsdebugx(1, " UPS returning %d bytes of data...",len);
138
ret = upsread (recvbuf, len);
140
signal (SIGALRM, SIG_IGN);
142
strncpy(buf, recvbuf, len);
143
upsdebugx(1, " OK, read %d bytes of data",len);
146
upsdebugx(1, " OOPS less than %d chars read",len);
152
signal (SIGALRM, SIG_IGN);
156
upsdebugx(1, "UPS returned: command failed");
159
upsdebugx(1, "UPS returned: command succeeded!");
171
* sec_cmd(mode, command, buffer, length)
173
* Sends mode command to the UPS with the given data.
175
* Returns -1 if command fails
176
* 0 if command succeeds, but returns no data
177
* length of returned data if data is returned
179
int sec_cmd(const char mode, const char *command, char *msgbuf, int *buflen)
184
memset(msg, 0, sizeof(msg));
186
/* create the message string */
188
sprintf(msg, "%c%c%03d%s%s", SEC_MSG_STARTCHAR,
189
mode, (*buflen)+3, command, msgbuf);
192
sprintf(msg, "%c%c003%s", SEC_MSG_STARTCHAR,
195
upsdebugx(1, "PC-->UPS: \"%s\"",msg);
197
upsdebugx(1, " send returned: %d",ret);
199
if (ret == -1) return -1;
201
ret = sec_upsrecv(msg);
202
upsdebugx(1, " receive returned: %d",ret);
204
if (ret < 0) return -1;
207
strncpy(msgbuf, msg, ret);
208
upsdebugx(1, "UPS<--PC: \"%s\"",msg);
210
/* *(msgbuf+ret) = '\0';*/
216
/* set up the serial connection */
217
void setup_serial(const char *port)
222
/* The SEC protocol alows for different baud rates. My daddy told me
223
"Never assume ...", so we try the different rates to see which works. */
225
for (i=0; i<5; i++) {
226
upsdebugx(1, "Trying to connect at %d baud",baud_rates[i].name);
227
open_serial(port, baud_rates[i].rate);
228
upsdebugx(2, " sending bogus command...");
230
upsdebugx(2, " reading reply...");
231
ret = sec_upsrecv(temp);
232
if (ret == -1) break;
233
upsdebugx(2, " no connection.");
234
unlockport(upsfd, port);
237
printf("Can't talk to UPS on port %s!\n",port);
238
printf("Check the cabling and portname and try again\n");
245
* addquery(cmd, field, varnum)
247
* Records that sec variable <varnum> is supported by this UPS and should be
248
* queried as part of the regular update. We need to record which command
249
* (cmd) to send, and which <field> corresponds to the variable.
252
void addquery(char *cmd, int field, int varnum)
256
for (q=0; q<SEC_QUERYLIST_LEN; q++) {
257
if (sec_querylist[q].command == NULL) {
258
/* command has not been recorded yet */
259
sec_querylist[q].command = cmd;
260
upsdebugx(1, " Query %d is %s",q,cmd);
262
if (sec_querylist[q].command == cmd) {
263
sec_querylist[q].varnum[field-1] = varnum;
264
upsdebugx(1, " Querying varnum %d",varnum);
272
* sec_setinfo(varnum, value)
274
* Update variable number <varnum> to value <value> in the info array.
277
void sec_setinfo(int varnum, char *value)
279
upsdebugx(1, "Updating variable %d (%s), new value is \"",
280
varnum, sec_varlist[varnum].name);
282
if (sec_varlist[varnum].flags & FLAG_ENUM) {
283
upsdebugx(1, "%s\" (ENUM)",
284
sec_enumdata[sec_varlist[varnum].edi + atoi(value)].value);
285
setinfo(sec_varlist[varnum].infotag,
287
sec_enumdata[sec_varlist[varnum].edi + atoi(value)].value);
289
else if (sec_varlist[varnum].flags & FLAG_STRING) {
290
upsdebugx(1, "%s\" (STRING)", value);
291
setinfo(sec_varlist[varnum].infotag,
296
if (sec_varlist[varnum].unit != 1) {
297
upsdebugx(1, "%.1f\" (Float)",
298
atof(value) / sec_varlist[varnum].unit);
299
setinfo(sec_varlist[varnum].infotag,
301
atof(value) / sec_varlist[varnum].unit);
304
upsdebugx(1, "%d\" (NUM)", atoi(value));
305
setinfo(sec_varlist[varnum].infotag,
316
* There are a number of non-SEC variables that are functions of the real
317
* variables. We update them here.
319
* OFF - UPS is off - INFO_ALRM_OUTPUTOFF is "Output off"
320
* OL - UPS is online - INFO_OUT_SOURCE is "Normal"
321
* OB - UPS is on battery - INFO_OUT_SOURCE is "On Battery"
322
* BY - UPS is on bypass - INFO_OUT_SOURCE is "On Bypass"
324
* OVER - UPS is overloaded - INFO_ALRM_OVERLOAD is "UPS Overloaded"
325
* LB - UPS battery is low - INFO_BATT_STATUS is "Battery Low"
326
* RB - UPS replace battery - INFO_BATT_COND is "Replace"
328
* TRIM - UPS is trimming - INFO_OUT_SOURCE is "Reducing"
329
* BOOST - UPS is boosting - INFO_OUT_SOURCE is "Boosting"
331
* FSD - UPS is shutdown - INFO_ALRM_SYSOFF is "System off"
334
void update_pseudovars( void )
340
upsdebugx(1, "Synthesizing INFO_STATUS...");
341
/* the STATUS variable */
344
if (supported(INFO_ALRM_OUTPUTOFF) &&
345
strcmp(getdata(INFO_ALRM_OUTPUTOFF), "Output off") == 0)
346
strcat(status, "OFF ");
347
else if (supported(INFO_OUT_SOURCE)) {
348
if (strcmp(getdata(INFO_OUT_SOURCE), "Normal") == 0)
349
strcat(status, "OL ");
350
else if (strcmp(getdata(INFO_OUT_SOURCE), "On Battery") == 0)
351
strcat(status, "OB ");
352
else if (strcmp(getdata(INFO_OUT_SOURCE), "On Bypass") == 0)
353
strcat(status, "BY ");
354
else if (strcmp(getdata(INFO_OUT_SOURCE), "Reducing") == 0)
355
strcat(status, "OL TRIM ");
356
else if (strcmp(getdata(INFO_OUT_SOURCE), "Boosting") == 0)
357
strcat(status, "OL BOOST ");
360
if (supported(INFO_ALRM_OVERLOAD) &&
361
strcmp(getdata(INFO_ALRM_OVERLOAD), "UPS Overloaded") == 0)
362
strcat(status, "OVER ");
363
if (supported(INFO_BATT_STATUS) &&
364
strcmp(getdata(INFO_BATT_STATUS), "Battery Low") == 0)
365
strcat(status, "LB ");
366
if (supported(INFO_BATT_COND) &&
367
strcmp(getdata(INFO_BATT_COND), "Replace") == 0)
368
strcat(status, "RB ");
370
if (supported(INFO_ALRM_SYSOFF) &&
371
strcmp(getdata(INFO_ALRM_SYSOFF), "System off") == 0)
372
strcat(status, "RB ");
374
setinfo (INFO_STATUS, "%s", status);
376
upsdebugx(1, "Synthesizing averages...");
378
if (supported(INFO_ACFREQ)) {
380
v = getdata(INFO_IN_ACFREQ1); if (v != NULL) fsum += atof(v), n++;
381
v = getdata(INFO_IN_ACFREQ2); if (v != NULL) fsum += atof(v), n++;
382
v = getdata(INFO_IN_ACFREQ3); if (v != NULL) fsum += atof(v), n++;
383
setinfo(INFO_ACFREQ, "%.1f", fsum / n);
385
if (supported(INFO_UTILITY)) {
387
v = getdata(INFO_IN_VOLT1); if (v != NULL) fsum += atof(v), n++;
388
v = getdata(INFO_IN_VOLT2); if (v != NULL) fsum += atof(v), n++;
389
v = getdata(INFO_IN_VOLT3); if (v != NULL) fsum += atof(v), n++;
390
setinfo(INFO_UTILITY, "%.1f", fsum / n);
392
if (supported(INFO_CURRENT)) {
394
v = getdata(INFO_OUT_CURRENT1); if (v != NULL) fsum += atof(v), n++;
395
v = getdata(INFO_OUT_CURRENT2); if (v != NULL) fsum += atof(v), n++;
396
v = getdata(INFO_OUT_CURRENT3); if (v != NULL) fsum += atof(v), n++;
397
setinfo(INFO_CURRENT, "%.1f", fsum / n);
399
if (supported(INFO_LOADPCT)) {
401
v = getdata(INFO_OUT_LOADPCT1); if (v != NULL) fsum += atof(v), n++;
402
v = getdata(INFO_OUT_LOADPCT2); if (v != NULL) fsum += atof(v), n++;
403
v = getdata(INFO_OUT_LOADPCT3); if (v != NULL) fsum += atof(v), n++;
404
setinfo(INFO_LOADPCT, "%.1f", fsum / n);
406
if (supported(INFO_LOADPWR)) {
408
v = getdata(INFO_OUT_LOADPWR1); if (v != NULL) fsum += atof(v), n++;
409
v = getdata(INFO_OUT_LOADPWR2); if (v != NULL) fsum += atof(v), n++;
410
v = getdata(INFO_OUT_LOADPWR3); if (v != NULL) fsum += atof(v), n++;
411
setinfo(INFO_LOADPWR, "%.1f", fsum / n);
413
if (supported(INFO_OUTVOLT)) {
415
v = getdata(INFO_OUT_VOLT1); if (v != NULL) fsum += atof(v), n++;
416
v = getdata(INFO_OUT_VOLT2); if (v != NULL) fsum += atof(v), n++;
417
v = getdata(INFO_OUT_VOLT3); if (v != NULL) fsum += atof(v), n++;
418
setinfo(INFO_OUTVOLT, "%.1f", fsum / n);
423
/*-------------------------------------------------------------------------
425
* Here are the mandatory functions called from the shared main.c
428
int upsdrv_infomax(void)
433
void upsdrv_initinfo(void)
436
char avail_list[300],*a,*p;
438
/* find out which variables/commands this UPS supports */
440
sec_cmd(SEC_POLLCMD, SEC_AVAILP1, avail_list, &msglen);
441
p = avail_list + msglen;
442
if (p != avail_list) *p++ = ',';
443
sec_cmd(SEC_POLLCMD, SEC_AVAILP2, p, &msglen);
446
if (strlen(avail_list) == 0) fatalx("No available variables found!");
448
upsdebugx(1, "List of available vars: %s",avail_list);
450
/* scan list adding variables to info array */
452
e = 0; /* index into enumdata array */
453
while ((p = strtok(a, ",")) != NULL) {
455
v = atoi(p); /* variable number of supported variable */
457
/* don't bother adding a write-only variable */
458
if (sec_varlist[v].flags & FLAG_WONLY) continue;
460
upsdebugx(1, "Adding variable %d (%s)",v,sec_varlist[v].name);
461
addinfo(sec_varlist[v].infotag,
463
sec_varlist[v].flags,
466
if (sec_varlist[v].flags & FLAG_ENUM) {
467
/* find entries in enumdata for current variable */
468
while (sec_enumdata[e].type != sec_varlist[v].infotag) e++;
469
/* add entries for enumerated variable */
470
while (sec_enumdata[e].type == sec_varlist[v].infotag) {
471
upsdebugx(1, " adding enumval \"%s\" (%d)",sec_enumdata[e].value,e);
473
sec_enumdata[e].value,
475
sec_enumdata[e].type);
480
/* record this variable and its command in the query list */
481
addquery(sec_varlist[v].cmd, sec_varlist[v].field, v);
484
/* add some composite non-SEC variables as well */
485
addinfo(INFO_STATUS, "", 0, 0);
486
/* these are averages of the 3 different lines */
487
if (supported(INFO_IN_ACFREQ1) ||
488
supported(INFO_IN_ACFREQ2) ||
489
supported(INFO_IN_ACFREQ3)) addinfo(INFO_ACFREQ, "", 0, 0);
490
if (supported(INFO_IN_VOLT1) ||
491
supported(INFO_IN_VOLT2) ||
492
supported(INFO_IN_VOLT3)) addinfo(INFO_UTILITY, "", 0, 0);
493
if (supported(INFO_OUT_CURRENT1) ||
494
supported(INFO_OUT_CURRENT2) ||
495
supported(INFO_OUT_CURRENT3)) addinfo(INFO_CURRENT, "", 0, 0);
496
if (supported(INFO_OUT_LOADPCT1) ||
497
supported(INFO_OUT_LOADPCT2) ||
498
supported(INFO_OUT_LOADPCT3)) addinfo(INFO_LOADPCT, "", 0, 0);
499
if (supported(INFO_OUT_LOADPWR1) ||
500
supported(INFO_OUT_LOADPWR2) ||
501
supported(INFO_OUT_LOADPWR3)) addinfo(INFO_LOADPWR, "", 0, 0);
502
if (supported(INFO_OUT_VOLT1) ||
503
supported(INFO_OUT_VOLT2) ||
504
supported(INFO_OUT_VOLT3)) addinfo(INFO_OUTVOLT, "", 0, 0);
509
* upsdrv_updateinfo()
511
* For a SEC protocol UPS, we only need to query those variables that are
515
void upsdrv_updateinfo(void)
517
char retbuf[128],*r,*n;
518
int ret, q, retlen, f;
520
upsdebugx(1, "--------Updating--------------------------");
522
/* loop over each query (a single SEC poll command)*/
523
for (q=0; q<SEC_QUERYLIST_LEN; q++) {
524
if (sec_querylist[q].command == NULL) break;
526
upsdebugx(1, "Polling %s...", sec_querylist[q].command);
529
ret = sec_cmd(SEC_POLLCMD, sec_querylist[q].command, retbuf, &retlen);
531
upslog(LOG_WARNING, "Warning sending poll cmd \"%s\" to UPS (%d)",
532
sec_querylist[q].command, ret);
538
for (f=0; f<SEC_MAXFIELDS; f++) {
540
if (n != NULL) *n = '\0';
541
/* is field/variable supported? */
543
/* only update the value if it's changed */
544
if (strcmp(sec_varlist[sqv(q,f)].value, r) != 0) {
545
strcpy(sec_varlist[sqv(q,f)].value, r);
546
sec_setinfo(sqv(q,f), r);
549
if (n == NULL) break;
554
/* update the non-sec variables */
557
upsdebugx(1, "**Writing updated info");
562
void upsdrv_shutdown(void)
564
/* replace with a proper shutdown function */
565
fatalx("shutdown not supported");
569
void instcmd (int auxcmd, int dlen, char *data)
571
/* TODO: reply to upsd? */
574
case CMD_BTEST0: /* stop battery test */
577
case CMD_BTEST1: /* start battery test */
581
upslogx(LOG_INFO, "instcmd: unknown type 0x%04x", auxcmd);
586
void upsdrv_help(void)
590
void upsdrv_makevartable(void)
594
void upsdrv_banner(void)
596
printf("Network UPS Tools (%s) - SEC protocol UPS driver \n", UPS_VERSION);
597
printf ("\tDriver version %s\n", SEC_DRIVER_VERSION);
601
void upsdrv_initups(void)
604
/* upssend_delay = 100000; */
606
setup_serial(device_path);
609
/* to get variables and flags from the command line, use this:
611
* set flag foo : /bin/driver -x foo
612
* set variable 'cable' to '1234' : /bin/driver -x cable=1234
614
* to test flag foo in your code:
616
* if (testvar("foo"))
619
* to show the value of cable:
621
* printf("cable is set to %s\n", getval("cable"));
624
/* upsh.instcmd = instcmd; */