1
/* toshiba1500.c - model specific routines for Toshiba 1500 series units
3
$Id: toshiba1500.c,v 1.2 2000/11/24 10:41:30 cvs Exp $
5
Revision 1.2 2000/09/08 05:55:45 rpm
6
Rewrite receive path to make it deterministic, not timeout-driven.
7
Reduce getupsmsg wait to 1 second for faster updates.
8
Reduce stack-based buffer sizes to something reasonable.
11
Copyright (C) 2000 Kenneth Porter <shiva@well.com>
12
Copyright (C) 1999 Russell Kroll <rkroll@exploits.org>
14
Based on fentonups.c by Russell Kroll.
16
Thanks go to Dennis Kruep and Greg Mack of Toshiba for making the
17
protocol documentation available.
19
This program is free software; you can redistribute it and/or modify
20
it under the terms of the GNU General Public License as published by
21
the Free Software Foundation; either version 2 of the License, or
22
(at your option) any later version.
24
This program is distributed in the hope that it will be useful,
25
but WITHOUT ANY WARRANTY; without even the implied warranty of
26
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
27
GNU General Public License for more details.
29
You should have received a copy of the GNU General Public License
30
along with this program; if not, write to the Free Software
31
Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
43
#include <sys/types.h>
44
#include <sys/termios.h>
50
#include "upscommon.h"
55
extern int upsc_debug; /* set to debug protocol */
58
int sddelay = 90; /* wait 90 seconds for shutdown */
70
create_info(INFOMAX, shmok);
72
/* setup variables that should always exist */
73
addinfo (INFO_MFR, "Toshiba", FLAG_STRING, 0);
74
addinfo (INFO_MODEL, "1500", FLAG_STRING, 0);
75
addinfo (INFO_STATUS, "", FLAG_STRING, 0);
76
addinfo (INFO_OUTVOLT, "", 0, 0);
77
addinfo (INFO_UTILITY, "", 0, 0);
78
addinfo (INFO_LOADPCT, "", 0, 0);
79
addinfo (INFO_BATTPCT, "", 0, 0);
80
addinfo (INFO_STATUS, "", 0, 0);
81
addinfo (INFO_ACFREQ, "", 0, 0);
82
/* addinfo (INFO_OUTFREQ, "", 0, 0); */
85
/* retry sending message 3 times until ACK received or timeout
86
* return 0 on ACK, -1 on timeout
89
int waitforack (const char* message)
92
for (try_count = 0; try_count < 3; ++try_count)
95
upssend("%s", message);
96
if (upsrecv (temp, 3, ACK, "") > 0)
97
return 0; /* success */
99
return -1; /* retries exhausted */
102
void updatestatus (unsigned status)
104
static unsigned last_status = 0;
106
printf ("updatestatus(%X)\n", status);
107
if (status != last_status) {
108
char status_string[256] = "";
109
last_status = status;
110
if (0x10 & status) /* input undervoltage or blackout */
111
strcat (status_string,"OB ");
113
strcat (status_string,"OL "); /* online */
114
if (0x08 & status) /* low battery */
115
strcat (status_string,"LB ");
116
if (0x04 & status) /* bypass operation */
117
strcat (status_string,"BYP ");
118
if (0x02 & status) /* inverter operation */
119
strcat (status_string,"INV ");
120
if (0x01 & status) /* synchronous operation */
121
strcat (status_string,"SYNC ");
122
/* todo: check 0x20 bit (fault) and read fault word
123
* trim trailing blank
125
if (status_string[0])
126
/* something was appended */
127
status_string[strlen (status_string) - 1] = '\0';
128
setinfo(INFO_STATUS, "%s", status_string);
132
/* return 0 on success */
134
int select_toshiba (void)
136
/* send 1-1-ENQ ("select") */
137
if (-1 == waitforack ("11\005")) {
139
printf ("Bad reply to select\n");
145
/* create value selection command followed by BCC */
147
void compose_toshiba_command (char* buf, int buflen,
154
snprintf (buf, buflen - 1, "\002%c%s\003", command_type, address);
155
/* STX does not participate in BCC computation */
157
for (p = buf + 1; *p; ++p)
164
/* get one char (may be high ascii), with timeout */
173
sa.sa_handler = timeout;
174
sigemptyset (&sigmask);
175
sa.sa_mask = sigmask;
177
sigaction (SIGALRM, &sa, NULL);
181
ret = read (upsfd, &in, 1);
184
signal (SIGALRM, SIG_IGN);
187
nolongertimeout (); /* toggle flag */
194
/* return -1 on failure, 0 on success
195
* stores null-terminated result string in buf,
196
* stores status byte in *status
199
int get_toshiba_result (char buf[5],
209
/* wait for up to 10 chars of response */
210
if (upsrecv (temp, sizeof temp, ETX, "") < 1)
213
/* attempt to parse value */
214
if (STX != temp[0] || command_type != temp[1] ||
215
address[0] != temp[2] || address[1] != temp[3]) {
217
printf ("prefix wrong\n");
220
/* find the ETX (preceded by UPS status, followed by BCC) */
225
for (p = temp + 1; *p; ++p)
227
bcc ^= ETX; /* account for ETX eaten by upsrecv */
230
printf ("bad BCC, result = %02X\n", bcc);
233
/* looks good, store it */
234
statusp = temp + strlen (temp) - 1; /* status at end of message */
236
*statusp = '\0'; /* terminate result string */
237
strcpy (buf, temp + 4);
241
/* query a value from the UPS and update status bits
242
* returns value (0-999) on success, -1 on failure
245
int pollvalue (const char* address)
252
printf ("pollvalue(%s)\n", address);
254
memset (temp, '\0', sizeof temp);
260
compose_toshiba_command (temp, sizeof temp, 'M', address);
262
/* now send command and wait for ACK */
263
if (-1 == waitforack (temp)) {
265
printf ("Bad reply to command\n");
269
upssendchar (EOT); /* done selecting */
271
/* now poll for value */
273
upssend("%s", "10\005"); /* 1-0-ENQ */
275
for (try_count = 0; try_count < 3; ++try_count) {
276
if (-1 == get_toshiba_result (temp, &status, 'M', address)) {
277
upssend("%s", "10\025"); /* 1-0-NAK */
280
break; /* result ok */
282
upssendchar (EOT); /* signal UPS we're quitting */
283
if (3 == try_count) {
285
printf ("giving up on parameter %s\n", address);
288
updatestatus (status); /* status at end of message */
289
sscanf (temp,"%u",&value);
291
printf ("parameter %s has value '%s'\n", address, temp);
295
/* normal idle loop - keep up with the current state of the UPS */
296
void updateinfo (void)
298
unsigned value; /* parsed result */
299
double dvalue; /* same, but as a double */
301
#if 0 /* no poll result on 1500 */
306
/* output voltage in percent */
307
dvalue = 120 * pollvalue ("10");
309
/* convert % to volts of rated voltage */
310
setinfo(INFO_OUTVOLT, "%4.1f", dvalue / 100.0);
314
#if 0 /* can't select on 1500 */
315
/* input voltage in percent */
316
dvalue = pollvalue ("20");
318
/* convert % to volts of rated voltage */
319
setinfo(INFO_UTILITY, "%4.1f", dvalue / 100.0);
324
/* output current in percent (not used) */
325
value = pollvalue ("30");
327
setinfo(INFO_LOADPCT, "%u", value);
331
/* battery voltage in percent */
332
value = pollvalue ("40");
334
setinfo(INFO_BATTPCT, "%u", value);
338
/* input frequency in Hz * 10 */
339
dvalue = pollvalue ("50") / 10.0;
341
setinfo(INFO_ACFREQ, "%3.1f", dvalue);
346
/* output frequency in Hz * 10 (not used) */
352
/* open port at 1200 bps, 7E1 framing */
354
void open_toshiba (char* port)
358
open_serial (port, B1200);
361
tcgetattr (upsfd, &tio);
362
tio.c_cflag = CS7 | CLOCAL | CREAD | PARENB;
363
#ifdef HAVE_CFSETISPEED
364
cfsetispeed (&tio, B1200);
365
cfsetospeed (&tio, B1200);
367
tcsetattr (upsfd, TCSANOW, &tio);
369
/* timeouts are normal for this model,
372
flag_timeoutfailure = -1;
375
/* power down the attached load immediately */
376
void forceshutdown (char *port)
379
char command[10], reply[10], status;
381
/* address is 90 plus delay in 30 second intervals
382
* valid delay range is 30 seconds to 8 minutes
384
static const char address[] = "91";
386
upslogx(LOG_INFO, "Initiating UPS shutdown");
387
printf ("Initiating forced UPS shutdown!\n");
391
for (try_count = 0; try_count < 3; ++try_count) {
393
if (-1 == select_toshiba ()) {
398
compose_toshiba_command (command, sizeof command, 'W', address);
399
upssend("%s", command);
401
if (-1 == get_toshiba_result (reply, &status, 'C', address))
404
/* send confirmation */
405
compose_toshiba_command (command, sizeof command, 'C', address);
406
upssend("%s", command);
408
/* check for acknowledge of confirmation */
409
if (-1 == get_toshiba_result (reply, &status, 'W', address))
412
break; /* result ok */
414
upssendchar (EOT); /* signal UPS we're quitting */
415
if (3 == try_count) {
416
printf ("giving up on shutdown attempt\n");
421
printf ("Waiting for poweroff...\n");
423
printf ("Hmm, did the shutdown fail? Oh well...\n");
429
void usage (char *prog)
431
printf ("usage: %s [-h] [-d <num>] [-k] <device>\n", prog);
432
printf ("Example: %s /dev/ttyS0\n", prog);
436
void help (char *prog)
438
printf ("usage: %s [-h] [-d <num>] [-k] <device>\n", prog);
440
printf ("-d <num> - wait <num> seconds after sending shutdown command\n");
441
printf ("-h - display this help\n");
442
printf ("-k - force shutdown\n");
443
printf ("<device> - /dev entry corresponding to UPS port\n");
446
int main (int argc, char **argv)
448
char *portname, *prog;
451
printf ("Network UPS Tools - Toshiba 1500 series UPS driver 0.1 (%s)\n", UPS_VERSION);
452
openlog ("toshiba1500", LOG_PID, LOG_FACILITY);
456
while ((i = getopt (argc, argv, "+d:ghk:")) != EOF) {
459
sddelay = atoi (optarg);
462
forceshutdown (optarg);
468
upsc_debug = 1; /* turn on port debugging */
488
for (i = strlen (argv[0]); i >= 0; i--)
489
if (argv[0][i] == '/') {
490
portname = &argv[0][i+1];
494
if (portname == NULL) {
495
printf ("Unable to abbreviate %s\n", argv[0]);
499
snprintf (statefn, sizeof statefn, "%s/toshiba1500-%s", STATEPATH,
502
open_toshiba (argv[0]);
506
createmsgq (); /* try to create IPC message queue */
514
/* wait up to 1 second for a message from the upsd */
516
upslogx(LOG_INFO, "Received a message from upsd");