1 /*
2  * al175.c - NUT support for Eltek AL175 alarm module.
3  *           AL175 shall be in COMLI mode.
4  *
5  * Copyright (C) 2004-2013 Marine & Bridge Navigation Systems <http://mns.spb.ru>
6  * Author: Kirill Smelkov <kirr@mns.spb.ru>
7  *
8  * This program is free software; you can redistribute it and/or modify
9  * it under the terms of the GNU General Public License as published by
10  * the Free Software Foundation; either version 2 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
21  */
22 
23 
24 /*
25  * - NOTE the following document is referenced in this driver:
26  *
27  *	TE-36862-B4 "COMLI COMMUNICATION PROTOCOL IMPLEMENTED IN PRS SYSTEMS",
28  *	by Eltek A/S
29  *
30  *
31  * - AL175 debug levels:
32  *
33  *	1 user-level trace (status, instcmd, etc...)
34  * 	2 status decode errors
35  * 	3 COMLI proto handling errors
36  * 	4 raw IO trace
37  *
38  */
39 
40 #include "main.h"
41 #include "serial.h"
42 #include "timehead.h"
43 
44 #include <stddef.h>
45 #include <ctype.h>
46 #include <stdlib.h>
47 #include <stdio.h>
48 #include <unistd.h>
49 
50 #include "nut_stdint.h"
51 typedef	uint8_t byte_t;
52 
53 
54 #define DRIVER_NAME	"Eltek AL175/COMLI driver"
55 #define DRIVER_VERSION	"0.12"
56 
57 /* driver description structure */
58 upsdrv_info_t upsdrv_info = {
59 	DRIVER_NAME,
60 	DRIVER_VERSION,
61 	"Kirill Smelkov <kirr@mns.spb.ru>\n" \
62 	"Marine & Bridge Navigation Systems <http://mns.spb.ru>",
63 	DRV_EXPERIMENTAL,
64 	{ NULL }
65 };
66 
67 
68 #define	STX	0x02
69 #define	ETX	0x03
70 #define	ACK	0x06
71 
72 
73 /************
74  * RAW DATA *
75  ************/
76 
77 /**
78  * raw_data buffer representation
79  */
80 typedef struct {
81 	byte_t     *buf;	/*!< the whole buffer address	*/
82 	size_t      buf_size;	/*!< the whole buffer size	*/
83 
84 	byte_t     *begin;	/*!< begin of content		*/
85 	byte_t     *end;	/*!< one-past-end of content	*/
86 } raw_data_t;
87 
88 
89 /**
90  * pseudo-alloca raw_data buffer  (alloca is not in POSIX)
91  * @param  varp		ptr-to local raw_data_t variable to which to alloca
92  * @param  buf_array	array allocated on stack which will be used as storage
93  *			(must be auto-variable)
94  * @return alloca'ed memory as raw_data
95  *
96  * Example:
97  *
98  *	raw_data_t ack;
99  *	byte_t     ack_buf[8];
100  *
101  *	raw_alloc_onstack(&ack, ack_buf);
102  */
103 #define raw_alloc_onstack(varp, buf_array) do {		\
104 	(varp)->buf		= &(buf_array)[0];	\
105 	(varp)->buf_size	= sizeof(buf_array);	\
106 							\
107 	(varp)->begin		= (varp)->buf;		\
108 	(varp)->end		= (varp)->buf;		\
109 } while (0)
110 
111 
112 /**
113  * xmalloc raw buffer
114  * @param  size	size in bytes
115  * @return xmalloc'ed memory as raw_data
116  */
raw_xmalloc(size_t size)117 static raw_data_t raw_xmalloc(size_t size)
118 {
119 	raw_data_t data;
120 
121 	data.buf	= xmalloc(size);
122 	data.buf_size	= size;
123 
124 	data.begin	= data.buf;
125 	data.end	= data.buf;
126 
127 	return data;
128 }
129 
130 /**
131  * free raw_data buffer
132  * @param  buf	raw_data buffer to free
133  */
raw_free(raw_data_t * buf)134 static void raw_free(raw_data_t *buf)
135 {
136 	free(buf->buf);
137 
138 	buf->buf      = NULL;
139 	buf->buf_size = 0;
140 	buf->begin    = NULL;
141 	buf->end      = NULL;
142 }
143 
144 
145 /***************************************************************************/
146 
147 /***************
148  * COMLI types *
149  ***************/
150 
151 /**
152  * COMLI message header info
153  * @see 1. INTRODUCTION
154  */
155 typedef struct {
156 	int  id;	/*!< Id[1:2]		*/
157 	int  stamp;	/*!< Stamp[3]		*/
158 	int  type;	/*!< Mess Type[4]	*/
159 } msg_head_t;
160 
161 /**
162  * COMLI IO header info
163  * @see 1. INTRODUCTION
164  */
165 typedef struct {
166 	size_t  addr;	/*!< Addr[5:8]		*/
167 	size_t  len;	/*!< NOB[9:10]		*/
168 } io_head_t;
169 
170 /**
171  * maximum allowed io.len value
172  */
173 #define	IO_LEN_MAX	0xff
174 
175 /**
176  * COMLI header info
177  * @see 1. INTRODUCTION
178  */
179 typedef struct {
180 	msg_head_t  msg;	/*!< message header [1:4]	*/
181 	io_head_t   io;		/*!< io header [5:10]		*/
182 } comli_head_t;
183 
184 
185 
186 /******************
187  * MISC UTILITIES *
188  ******************/
189 
190 /**
191  * convert hex string to int
192  * @param  head  input string
193  * @param  len   string length
194  * @return parsed value (>=0) if success, -1 on error
195  */
from_hex(const byte_t * head,unsigned len)196 static long from_hex(const byte_t *head, unsigned len)
197 {
198 	long val=0;
199 
200 	while (len-- != 0) {
201 		int ch = *head;
202 
203 		if (!isxdigit(ch))
204 			return -1;	/* wrong character	*/
205 
206 		val *= 0x10;
207 
208 		if (isdigit(ch)) {
209 			val += (ch-'0');
210 		}
211 		else {
212 			/* ch = toupper(ch)  without locale-related problems */
213 			if (ch < 'A')
214 				ch += 'A' - 'a';
215 
216 			val += 0x0A + (ch-'A');
217 		}
218 
219 		++head;
220 	}
221 
222 	return val;
223 }
224 
225 /**
226  * compute checksum of a buffer
227  * @see 10. CHECKSUM BCC
228  * @param   buf     buffer address
229  * @param   count   no. of bytes in the buffer
230  * @return  computed checksum
231  */
compute_bcc(const byte_t * buf,size_t count)232 static byte_t compute_bcc(const byte_t *buf, size_t count)
233 {
234 	byte_t bcc=0;
235 	size_t i;
236 
237 	for (i=0; i<count; ++i)
238 		bcc ^= buf[i];
239 
240 	return bcc;
241 }
242 
243 
244 /**
245  * reverse bits in a buffer bytes from right to left
246  * @see 6. CODING AND DECODING OF REGISTER VALUES
247  * @param   buf     buffer address
248  * @param   count   no. of bytes in the buffer
249  */
reverse_bits(byte_t * buf,size_t count)250 static void reverse_bits(byte_t *buf, size_t count)
251 {
252 	byte_t x;
253 
254 	while (count!=0) {
255 		x = *buf;
256 		x = (byte_t)( (x & 0x80) >> 7 )  |
257 		    (byte_t)( (x & 0x40) >> 5 )  |
258 		    (byte_t)( (x & 0x20) >> 3 )  |
259 		    (byte_t)( (x & 0x10) >> 1 )  |
260 		    (byte_t)( (x & 0x08) << 1 )  |
261 		    (byte_t)( (x & 0x04) << 3 )  |
262 		    (byte_t)( (x & 0x02) << 5 )  |
263 		    (byte_t)( (x & 0x01) << 7 );
264 		*buf = x;
265 
266 		++buf;
267 		--count;
268 	}
269 }
270 
271 
272 /********************************************************************/
273 
274 /*
275  * communication basics
276  *
277  * ME	(Monitor Equipment)
278  * PRS	(Power Rectifier System)  /think of it as of UPS in common speak/
279  *
280  * there are 2 types of transactions:
281  *
282  * 'ACTIVATE COMMAND'
283  *	ME -> PRS		(al_prep_activate)
284  *	ME <- PRS [ack]		(al_check_ack)
285  *
286  *
287  * 'READ REGISTER'
288  *	ME -> PRS		(al_prep_read_req)
289  *	ME <- PRS [data]	(al_parse_reply)
290  *
291  */
292 
293 /********************
294  * COMLI primitives *
295  ********************/
296 
297 
298 /************************
299  * COMLI: OUTPUT FRAMES *
300  ************************/
301 
302 /**
303  * prepare COMLI sentence
304  * @see 1. INTRODUCTION
305  * @param   dest    [out] where to put the result
306  * @param   h       COMLI header info
307  * @param   buf     data part of the sentence
308  * @param   count   amount of data bytes in the sentence
309  *
310  * @note: the data are copied into the sentence "as-is", there is no conversion is done.
311  *  if the caller wants to reverse bits it is necessary to call reverse_bits(...) prior
312  *  to comli_prepare.
313  */
comli_prepare(raw_data_t * dest,const comli_head_t * h,const void * buf,size_t count)314 static void comli_prepare(raw_data_t *dest, const comli_head_t *h, const void *buf, size_t count)
315 {
316 /*
317  *    0     1   2      3       4     5     6     7     8       9    10    11 - - -     N-1    N
318  * +-----+---------+-------+------+-------------------------+-----------+------------+-----+-----+
319  * | STX | IDh IDl | Stamp | type | addr1 addr2 addr3 addr4 | NOBh NOBl | ...data... | ETX | BCC |
320  * +-----+---------+-------+------+-------------------------+-----------+------------+-----+-----+
321  *
322  * ^                                                                                             ^
323  * |                                                                                             |
324  *begin                                                                                         end
325  */
326 	byte_t *out = dest->begin;
327 
328 
329 	/* it's caller responsibility to allocate enough space.
330 	   else it is a bug in the program */
331 	if ( (out+11+count+2) > (dest->buf + dest->buf_size) )
332 		fatalx(EXIT_FAILURE, "too small dest in comli_prepare\n");
333 
334 	out[0] = STX;
335 	snprintf((char *)out+1, 10+1, "%02X%1i%1i%04zX%02zX", h->msg.id, h->msg.stamp, h->msg.type, h->io.addr, h->io.len);
336 
337 	memcpy(out+11, buf, count);
338 	reverse_bits(out+11, count);
339 
340 
341 	out[11+count] = ETX;
342 	out[12+count] = compute_bcc(out+1, 10+count+1);
343 
344 	dest->end = dest->begin + (11+count+2);
345 }
346 
347 
348 
349 
350 /**
351  * prepare AL175 read data request
352  * @see 2. MESSAGE TYPE 2 (COMMAND SENT FROM MONITORING EQUIPMENT)
353  * @param   dest    [out] where to put the result
354  * @param   addr    start address of requested area
355  * @param   count   no. of requested bytes
356  */
al_prep_read_req(raw_data_t * dest,size_t addr,size_t count)357 static void al_prep_read_req(raw_data_t *dest, size_t addr, size_t count)
358 {
359 	comli_head_t h;
360 
361 	h.msg.id	= 0x14;
362 	h.msg.stamp	= 1;
363 	h.msg.type	= 2;
364 
365 	h.io.addr	= addr;
366 	h.io.len	= count;
367 
368 	comli_prepare(dest, &h, NULL, 0);
369 }
370 
371 
372 /**
373  * prepare AL175 activate command
374  * @see 4. MESSAGE TYPE 0 (ACTIVATE COMMAND)
375  * @param   dest    [out] where to put the result
376  * @param   cmd     command type        [11]
377  * @param   subcmd  command subtype     [12]
378  * @param   pr1     first parameter     [13:14]
379  * @param   pr2     second parameter    [15:16]
380  * @param   pr3     third parameter     [17:18]
381  */
al_prep_activate(raw_data_t * dest,byte_t cmd,byte_t subcmd,uint16_t pr1,uint16_t pr2,uint16_t pr3)382 static void al_prep_activate(raw_data_t *dest, byte_t cmd, byte_t subcmd, uint16_t pr1, uint16_t pr2, uint16_t pr3)
383 {
384 	comli_head_t h;
385 	char data[8+1];
386 
387 	h.msg.id	= 0x14;
388 	h.msg.stamp	= 1;
389 	h.msg.type	= 0;
390 
391 	h.io.addr	= 0x4500;
392 	h.io.len	= 8;
393 
394 	/* NOTE: doc says we should use ASCII coding here, but the actual
395 	 *       values are > 0x80, so we use binary coding. And have to
396 	 *       make this "fit" into the char array required by snprintf */
397 	data[0] = (char)cmd;
398 	data[1] = (char)subcmd;
399 
400 	/* FIXME? One CI testcase builder claims here that
401 	 *   warning: '%2X' directive output may be truncated writing
402 	 *   between 2 and 4 bytes into a region of size between 3 and 5
403 	 *   [-Wformat-truncation=]
404 	 * but none others do, and I can't figure out how it thinks so :/
405 	 *
406 	 * Per https://stackoverflow.com/questions/51534284/how-to-circumvent-format-truncation-warning-in-gcc
407 	 * https://www.mail-archive.com/gcc-bugs@gcc.gnu.org/msg521037.html
408 	 * and simlar googlable sources, this seems to be a bug-or-feature
409 	 * linked to non-zero optimization level and/or not checking for the
410 	 * return value (conveys runtime errors if any do happen).
411 	 */
412 	assert (pr1 <= UINT8_MAX);
413 	assert (pr2 <= UINT8_MAX);
414 	assert (pr3 <= UINT8_MAX);
415 	if (0 > snprintf(data+2, 6+1, "%2X%2X%2X", pr1, pr2, pr3)) {
416 		data[8] = '\0';
417 	}
418 
419 	comli_prepare(dest, &h, data, 8);
420 }
421 
422 /***********************
423  * COMLI: INPUT FRAMES *
424  ***********************/
425 
426 /**
427  * check COMLI frame for correct layout and bcc
428  * @param  f	frame to check
429  *
430  * @return 0 (ok)  -1 (error)
431  */
comli_check_frame(raw_data_t f)432 static int comli_check_frame(/*const*/ raw_data_t f)
433 {
434 	int bcc;
435 	byte_t *tail;
436 
437 	if ( (f.end - f.begin) < 2 )
438 		return -1;
439 
440 	if (*f.begin!=STX)
441 		return -1;
442 
443 	tail = f.end - 2;
444 	if (tail <= f.begin)
445 		return -1;
446 
447 	if (tail[0]!=ETX)
448 		return -1;
449 
450 	bcc = compute_bcc(f.begin+1, (size_t)(f.end - f.begin) - 2 /*STX & BCC*/);
451 	if (bcc!= tail[1])
452 		return -1;
453 
454 	return 0;
455 }
456 
457 
458 /**
459  * parse reply header from PRS
460  * @see 3. MESSAGE TYPE 0 (REPLY FROM PRS ON MESSAGE TYPE 2)
461  *
462  * @param  io			[out] parsed io_header
463  * @param  raw_reply_head	[in] raw reply header from PRS
464  * @return 0 (ok),  -1 (error)
465  *
466  * @see al_parse_reply
467  */
al_parse_reply_head(io_head_t * io,const raw_data_t raw_reply_head)468 static int al_parse_reply_head(io_head_t *io, const raw_data_t raw_reply_head)
469 {
470 /*
471  *    0     1   2      3       4     5     6     7     8       9    10
472  * +-----+---------+-------+------+-------------------------+-----------+-----------+
473  * | STX | IDh IDl | Stamp | type | addr1 addr2 addr3 addr4 | NOBh NOBl | ......... |
474  * +-----+---------+-------+------+-------------------------+-----------+-----------+
475  *
476  *       ^                                                              ^
477  *       |                                                              |
478  *     begin							       end
479  */
480 
481 	size_t io_addr, io_len;
482 	const byte_t *reply_head = raw_reply_head.begin - 1;
483 
484 	if ( (raw_reply_head.end - raw_reply_head.begin) != 10)  {
485 		upsdebugx(3, "%s: wrong size\t(%i != 10)", __func__, (int)(raw_reply_head.end - raw_reply_head.begin));
486 		return -1;		/* wrong size	*/
487 	}
488 
489 	if (reply_head[1]!='0' || reply_head[2]!='0')  {
490 		upsdebugx(3, "%s: wrong id\t('%c%c' != '00')", __func__, reply_head[1], reply_head[2]);
491 		return -1;		/* wrong id	*/
492 	}
493 
494 	if (reply_head[3]!='1')  {
495 		upsdebugx(3, "%s: wrong stamp\t('%c' != '1')", __func__, reply_head[3]);
496 		return -1;		/* wrong stamp	*/
497 	}
498 
499 	if (reply_head[4]!='0')  {
500 		upsdebugx(3, "%s: wrong type\t('%c' != '0')", __func__, reply_head[4]);
501 		return -1;		/* wrong type	*/
502 	}
503 
504 	/* Avoid signed/unsigned implicit conversion warnings
505 	 * At least, when shuffling a signed long into unsigned long,
506 	 * don't have to worry about overflows */
507 	io_addr = (size_t)from_hex(&reply_head[5], 4);
508 	if (io_addr == -1UL)  {
509 		upsdebugx(3, "%s: invalid addr\t('%c%c%c%c')", __func__,
510 			reply_head[5], reply_head[6], reply_head[7], reply_head[8]);
511 		return -1;		/* wrong addr	*/
512 	}
513 
514 	io_len = (size_t)from_hex(&reply_head[9], 2);
515 	if (io_len == -1UL)   {
516 		upsdebugx(3, "%s: invalid nob\t('%c%c')", __func__, reply_head[9], reply_head[10]);
517 		return -1;		/* wrong NOB	*/
518 	}
519 
520 	if (io_len > IO_LEN_MAX) {
521 		upsdebugx(3, "nob too big\t(%zu > %i)", io_len, IO_LEN_MAX);
522 		return -1;		/* too much data claimed */
523 	}
524 
525 	io->addr = io_addr;
526 	io->len  = io_len;
527 
528 
529 	return 0;
530 }
531 
532 
533 /**
534  * parse reply from PRS
535  * @see 3. MESSAGE TYPE 0 (REPLY FROM PRS ON MESSAGE TYPE 2)
536  * @param  io_head	[out] parsed io_header
537  * @param  io_buf	[in] [out] raw_data where to place incoming data (see ...data... below)
538  * @param  raw_reply	raw reply from PRS to check
539  * @return  0 (ok), -1 (error)
540  *
541  * @see al_parse_reply_head
542  */
al_parse_reply(io_head_t * io_head,raw_data_t * io_buf,raw_data_t raw_reply)543 static int al_parse_reply(io_head_t *io_head, raw_data_t *io_buf, /*const*/ raw_data_t raw_reply)
544 {
545 /*
546  *    0     1   2      3       4     5     6     7     8       9    10    11 - - -     N-1    N
547  * +-----+---------+-------+------+-------------------------+-----------+------------+-----+-----+
548  * | STX | IDh IDl | Stamp | type | addr1 addr2 addr3 addr4 | NOBh NOBl | ...data... | ETX | BCC |
549  * +-----+---------+-------+------+-------------------------+-----------+------------+-----+-----+
550  *
551  *       ^                                                                           ^
552  *       |                                                                           |
553  *     begin                                                                        end
554  */
555 
556 	int err;
557 	size_t i;
558 	const byte_t *reply = NULL;
559 
560 	/* 1: extract header and parse it */
561 	/*const*/ raw_data_t raw_reply_head = raw_reply;
562 
563 	if (raw_reply_head.begin + 10 <= raw_reply_head.end)
564 		raw_reply_head.end = raw_reply_head.begin + 10;
565 
566 	err = al_parse_reply_head(io_head, raw_reply_head);
567 	if (err==-1)
568 		return -1;
569 
570 
571 	/* 2: process data */
572 	reply = raw_reply.begin - 1;
573 
574 	if ( (raw_reply.end - raw_reply.begin) != (ptrdiff_t)(10 + io_head->len))  {
575 		upsdebugx(3, "%s: corrupt sentence\t(%i != %zi)",
576 				__func__, (int)(raw_reply.end - raw_reply.begin), 10 + io_head->len);
577 		return -1;		/* corrupt sentence	*/
578 	}
579 
580 
581 	/* extract the data */
582 	if (io_buf->buf_size < io_head->len)	{
583 		upsdebugx(3, "%s: too much data to fit in io_buf\t(%zu > %zu)",
584 				__func__, io_head->len, io_buf->buf_size);
585 		return -1;		/* too much data to fit in io_buf	*/
586 	}
587 
588 	io_buf->begin = io_buf->buf;
589 	io_buf->end   = io_buf->begin;
590 
591 	for (i=0; i<io_head->len; ++i)
592 		*(io_buf->end++) = reply[11+i];
593 
594 	assert(io_buf->end - io_buf->begin >= 0);
595 	size_t io_buf_len = (size_t)(io_buf->end - io_buf->begin);
596 	reverse_bits(io_buf->begin, io_buf_len );
597 
598 	upsdebug_hex(3, "\t\t--> payload", io_buf->begin, io_buf_len);
599 
600 	return 0;	/* all ok */
601 }
602 
603 
604 /**
605  * check acknowledge from PRS
606  * @see 5. ACKNOWLEDGE FROM PRS
607  * @param   raw_ack  raw acknowledge from PRS to check
608  * @return  0 on success, -1 on error
609  */
al_check_ack(raw_data_t raw_ack)610 static int al_check_ack(/*const*/ raw_data_t raw_ack)
611 {
612 /*
613  *    0     1   2      3       4     5     6     7
614  * +-----+---------+-------+------+-----+-----+-----+
615  * | STX | IDh IDl | Stamp | type | ACK | ETX | BCC |
616  * +-----+---------+-------+------+-----+-----+-----+
617  *
618  *       ^                              ^
619  *       |                              |
620  *     begin                           end
621  */
622 
623 	const byte_t *ack = raw_ack.begin - 1;
624 
625 	if ( (raw_ack.end - raw_ack.begin) !=5)  {
626 		upsdebugx(3, "%s: wrong size\t(%i != 5)", __func__, (int)(raw_ack.end - raw_ack.begin));
627 		return -1;		/* wrong size	*/
628 	}
629 
630 	if (ack[1]!='0' || ack[2]!='0')  {
631 		upsdebugx(3, "%s: wrong id\t('%c%c' != '00')", __func__, ack[1], ack[2]);
632 		return -1;		/* wrong id	*/
633 	}
634 
635 	/* the following in not mandated. it is just said it will be
636 	 * "same as one received". but we always send '1' (0x31) as stamp
637 	 * (see 4. MESSAGE TYPE 0 (ACTIVATE COMMAND). Hence, stamp checking
638 	 * is hardcoded here.
639 	 */
640 	if (ack[3]!='1')  {
641 		upsdebugx(3, "%s: wrong stamp\t('%c' != '1')", __func__, ack[3]);
642 		return -1;		/* wrong stamp	*/
643 	}
644 
645 	if (ack[4]!='1')  {
646 		upsdebugx(3, "%s: wrong type\t('%c' != '1')", __func__, ack[4]);
647 		return -1;		/* wrong type	*/
648 	}
649 
650 	if (ack[5]!=ACK)  {
651 		upsdebugx(3, "%s: wrong ack\t(0x%02X != 0x%02X)", __func__, ack[5], ACK);
652 		return -1;		/* wrong ack	*/
653 	}
654 
655 
656 	return 0;
657 }
658 
659 
660 
661 
662 
663 /******************************************************************/
664 
665 
666 /**********
667  * SERIAL *
668  **********/
669 
670 /* clear any flow control (copy from powercom.c) */
ser_disable_flow_control(void)671 static void ser_disable_flow_control (void)
672 {
673 	struct termios tio;
674 
675 	tcgetattr (upsfd, &tio);
676 
677 	/* Clumsy rewrite of a one-liner
678 	 *   tio.c_iflag &= ~ (IXON | IXOFF);
679 	 * to avoid type conversion warnings */
680 	tcflag_t x = (IXON | IXOFF);
681 	tio.c_iflag &= ~ x;
682 	tio.c_cc[VSTART] = _POSIX_VDISABLE;
683 	tio.c_cc[VSTOP] = _POSIX_VDISABLE;
684 
685 	upsdebugx(4, "Flow control disable");
686 
687 	/* disable any flow control */
688 	tcsetattr(upsfd, TCSANOW, &tio);
689 }
690 
flush_rx_queue()691 static void flush_rx_queue()
692 {
693 	ser_flush_in(upsfd, "", /*verbose=*/nut_debug_level);
694 }
695 
696 /**
697  * transmit frame to PRS
698  *
699  * @param  dmsg   debug message prefix
700  * @param  frame  the frame to tansmit
701  * @return 0 (ok) -1 (error)
702  */
tx(const char * dmsg,raw_data_t frame)703 static int tx(const char *dmsg, /*const*/ raw_data_t frame)
704 {
705 	ssize_t err;
706 
707 	assert(frame.end - frame.begin >= 0);
708 	size_t frame_len = (size_t)(frame.end - frame.begin);
709 
710 	upsdebug_ascii(3, dmsg, frame.begin, frame_len);
711 
712 	err = ser_send_buf(upsfd, frame.begin, frame_len );
713 	if (err==-1) {
714 		upslogx(LOG_ERR, "failed to send frame to PRS: %s", strerror(errno));
715 		return -1;
716 	}
717 
718 	if (err != (ssize_t)frame_len) {
719 		upslogx(LOG_ERR, "sent incomplete frame to PRS");
720 		return -1;
721 	}
722 
723 	return 0;
724 }
725 
726 /***********
727  * CHATTER *
728  ***********/
729 
730 static time_t T_io_begin;	/* start of current I/O transaction */
731 static int    T_io_timeout;	/* in seconds */
732 
733 /* start new I/O transaction with maximum time limit */
io_new_transaction(int timeout)734 static void io_new_transaction(int timeout)
735 {
736 	T_io_begin = time(NULL);
737 	T_io_timeout = timeout;
738 }
739 
740 /**
741  * get next character from input stream
742  *
743  * @param  ch   ptr-to where store result
744  *
745  * @return -1 (error)  0 (timeout)  >0 (got it)
746  *
747  */
get_char(char * ch)748 static ssize_t get_char(char *ch)
749 {
750 	time_t now = time(NULL);
751 	long rx_timeout;
752 
753 	rx_timeout = T_io_timeout - (now - T_io_begin);
754 	/* negative rx_timeout -> time already out */
755 	if (rx_timeout < 0)
756 		return 0;
757 	return ser_get_char(upsfd, ch, rx_timeout, 0);
758 }
759 
760 
761 /**
762  * get next characters from input stream
763  *
764  * @param   buf ptr-to output buffer
765  * @param   len buffer length
766  *
767  * @return -1 (error)  0 (timeout)  >0 (no. of characters actually read)
768  *
769  */
get_buf(byte_t * buf,size_t len)770 static ssize_t get_buf(byte_t *buf, size_t len)
771 {
772 	time_t now = time(NULL);
773 	long rx_timeout;
774 
775 	rx_timeout = T_io_timeout - (now - T_io_begin);
776 	/* negative rx_timeout -> time already out */
777 	if (rx_timeout < 0)
778 		return 0;
779 	return ser_get_buf_len(upsfd, buf, len, rx_timeout, 0);
780 }
781 
782 /**
783  * scan incoming bytes for specific character
784  *
785  * @return 0 (got it) -1 (error)
786  */
scan_for(char c)787 static int scan_for(char c)
788 {
789 	char in;
790 	ssize_t  err;
791 
792 	while (1) {
793 		err = get_char(&in);
794 		if (err==-1 || err==0 /*timeout*/)
795 			return -1;
796 
797 		if (in==c)
798 			break;
799 	}
800 
801 	return 0;
802 }
803 
804 
805 /**
806  * receive 'activate command' ACK from PRS
807  *
808  * @return 0 (ok) -1 (error)
809  */
recv_command_ack()810 static int recv_command_ack()
811 {
812 	ssize_t err;
813 	raw_data_t ack;
814 	byte_t     ack_buf[8];
815 
816 	/* 1:  STX  */
817 	err = scan_for(STX);
818 	if (err==-1)
819 		return -1;
820 
821 
822 	raw_alloc_onstack(&ack, ack_buf);
823 	*(ack.end++) = STX;
824 
825 
826 	/* 2:  ID1 ID2 STAMP MSG_TYPE ACK ETX BCC */
827 	err = get_buf(ack.end, 7);
828 	if (err!=7)
829 		return -1;
830 
831 	ack.end += 7;
832 
833 	/* frame constructed - let's verify it */
834 	assert (ack.end - ack.begin >= 0);
835 	upsdebug_ascii(3, "rx (ack):\t\t", ack.begin, (size_t)(ack.end - ack.begin));
836 
837 	/* generic layout */
838 	err = comli_check_frame(ack);
839 	if (err==-1)
840 		return -1;
841 
842 	/* shrink frame */
843 	ack.begin += 1;
844 	ack.end   -= 2;
845 
846 	return al_check_ack(ack);
847 }
848 
849 /**
850  * receive 'read register' data from PRS
851  * @param  io		[out] io header of received data
852  * @param  io_buf	[in] [out] where to place incoming data
853  *
854  * @return 0 (ok) -1 (error)
855  */
recv_register_data(io_head_t * io,raw_data_t * io_buf)856 static int recv_register_data(io_head_t *io, raw_data_t *io_buf)
857 {
858 	ssize_t err;
859 	int ret;
860 	raw_data_t reply_head;
861 	raw_data_t reply;
862 
863 	byte_t     reply_head_buf[11];
864 
865 	/* 1:  STX  */
866 	err = scan_for(STX);
867 	if (err==-1)
868 		return -1;
869 
870 	raw_alloc_onstack(&reply_head, reply_head_buf);
871 	*(reply_head.end++) = STX;
872 
873 
874 	/* 2:  ID1 ID2 STAMP MSG_TYPE ADDR1 ADDR2 ADDR3 ADDR4 LEN1 LEN2 */
875 	err = get_buf(reply_head.end, 10);
876 	if (err!=10)
877 		return -1;
878 
879 	reply_head.end += 10;
880 
881 	assert (reply_head.end - reply_head.begin >= 0);
882 	upsdebug_ascii(3, "rx (head):\t", reply_head.begin, (size_t)(reply_head.end - reply_head.begin));
883 
884 
885 	/* 3:  check header, extract IO info */
886 	reply_head.begin += 1;	/* temporarily strip STX */
887 
888 	err = al_parse_reply_head(io, reply_head);
889 	if (err==-1)
890 		return -1;
891 
892 	reply_head.begin -= 1;  /* restore STX */
893 
894 	upsdebugx(4, "\t\t--> addr: 0x%zx  len: 0x%zx", io->addr, io->len);
895 
896 	/* 4:  allocate space for full reply and copy header there */
897 	reply = raw_xmalloc(11/*head*/ + io->len/*data*/ + 2/*ETX BCC*/);
898 
899 	assert (reply_head.end - reply_head.begin >= 0);
900 	size_t reply_head_len = (size_t)(reply_head.end - reply_head.begin);
901 
902 	memcpy(reply.end, reply_head.begin, reply_head_len);
903 	reply.end += reply_head_len;
904 
905 	/* 5:  receive tail of the frame */
906 	err = get_buf(reply.end, io->len + 2);
907 	if (err!=(int)(io->len+2)) {
908 		upsdebugx(4, "rx_tail failed, err=%zi (!= %zi)", err, io->len+2);
909 		ret = -1; goto out;
910 	}
911 
912 	reply.end += io->len + 2;
913 
914 
915 	/* frame constructed, let's verify it */
916 	assert (reply.end - reply.begin >= 0);
917 	upsdebug_ascii(3, "rx (head+data):\t", reply.begin, (size_t)(reply.end - reply.begin));
918 
919 	/* generic layout */
920 	err = comli_check_frame(reply);
921 	if (err==-1) {
922 		upsdebugx(3, "%s: corrupt frame", __func__);
923 		ret = -1; goto out;
924 	}
925 
926 	/* shrink frame */
927 	reply.begin += 1;
928 	reply.end   -= 2;
929 
930 
931 	/* XXX: a bit of processing duplication here	*/
932 	ret = al_parse_reply(io, io_buf, reply);
933 
934 out:
935 	raw_free(&reply);
936 	return ret;
937 }
938 
939 
940 /*****************************************************************/
941 
942 /*********************
943  * AL175: DO COMMAND *
944  *********************/
945 
946 /**
947  * do 'ACTIVATE COMMAND'
948  *
949  * @return 0 (ok)  -1 (error)
950  */
al175_do(byte_t cmd,byte_t subcmd,uint16_t pr1,uint16_t pr2,uint16_t pr3)951 static int al175_do(byte_t cmd, byte_t subcmd, uint16_t pr1, uint16_t pr2, uint16_t pr3)
952 {
953 	int err;
954 	raw_data_t CTRL_frame;
955 	byte_t	   CTRL_frame_buf[512];
956 
957 	raw_alloc_onstack(&CTRL_frame, CTRL_frame_buf);
958 	al_prep_activate(&CTRL_frame, cmd, subcmd, pr1, pr2, pr3);
959 
960 	flush_rx_queue();		        /*  DROP  */
961 
962 	err = tx("tx (ctrl):\t", CTRL_frame);	/*  TX    */
963 	if (err==-1)
964 		return -1;
965 
966 
967 	return recv_command_ack();	        /*  RX    */
968 }
969 
970 
971 /**
972  * 'READ REGISTER'
973  *
974  */
al175_read(byte_t * dst,size_t addr,size_t count)975 static int al175_read(byte_t *dst, size_t addr, size_t count)
976 {
977 	int err;
978 	raw_data_t REQ_frame;
979 	raw_data_t rx_data;
980 	io_head_t io;
981 
982 	byte_t	REQ_frame_buf[512];
983 
984 	raw_alloc_onstack(&REQ_frame, REQ_frame_buf);
985 	al_prep_read_req(&REQ_frame, addr, count);
986 
987 	flush_rx_queue();			/*  DROP  */
988 
989 	err = tx("tx (req):\t", REQ_frame);	/*  TX    */
990 	if (err==-1)
991 		return -1;
992 
993 
994 	rx_data.buf	 = dst;
995 	rx_data.buf_size = count;
996 	rx_data.begin	 = dst;
997 	rx_data.end	 = dst;
998 
999 	err = recv_register_data(&io, &rx_data);
1000 	if (err==-1)
1001 		return -1;
1002 
1003 	if ((rx_data.end - rx_data.begin) < 0 ||
1004 	    (size_t)(rx_data.end - rx_data.begin) != count)
1005 		return -1;
1006 
1007 	if ( (io.addr != addr) || (io.len != count) ) {
1008 		upsdebugx(3, "%s: io_head mismatch\t(%zx,%zx != %zx,%zx)",
1009 				__func__, io.addr, io.len, addr, count);
1010 		return -1;
1011 	}
1012 
1013 
1014 	return 0;
1015 }
1016 
1017 /*************
1018  * NUT STUFF *
1019  *************/
1020 
1021 /****************************
1022  * ACTIVATE COMMANDS table
1023  *
1024  * see 8. ACTIVATE COMMANDS
1025  */
1026 
1027 typedef uint16_t mm_t;	/* minutes */
1028 typedef uint16_t VV_t;	/* voltage */
1029 
1030 #define	Z1  , 0
1031 #define Z2  , 0, 0
1032 #define	Z3  , 0, 0, 0
1033 
1034 #define	ACT int
1035 
1036 /* Declare to keep compiler happy even if some routines below are not used currently */
1037 ACT	TOGGLE_PRS_ONOFF	(void);
1038 ACT	CANCEL_BOOST		(void);
1039 ACT	STOP_BATTERY_TEST	(void);
1040 ACT	START_BATTERY_TEST	(VV_t EndVolt, mm_t Minutes);
1041 ACT	SET_FLOAT_VOLTAGE	(VV_t v);
1042 ACT	SET_BOOST_VOLTAGE	(VV_t v);
1043 ACT	SET_HIGH_BATTERY_LIMIT	(VV_t Vhigh);
1044 ACT	SET_LOW_BATTERY_LIMIT	(VV_t Vlow);
1045 ACT	SET_DISCONNECT_LEVEL_AND_DELAY	(VV_t level, mm_t delay);
1046 ACT	RESET_ALARMS		(void);
1047 ACT	CHANGE_COMM_PROTOCOL	(void);
1048 ACT	SET_VOLTAGE_AT_ZERO_T	(VV_t v);
1049 ACT	SET_SLOPE_AT_ZERO_T	(VV_t mv_per_degree);
1050 ACT	SET_MAX_TCOMP_VOLTAGE	(VV_t v);
1051 ACT	SET_MIN_TCOMP_VOLTAGE	(VV_t v);
1052 ACT	SWITCH_TEMP_COMP	(uint16_t on);
1053 ACT	SWITCH_SYM_ALARM	(void);
1054 
1055 /* Implement */
TOGGLE_PRS_ONOFF()1056 ACT	TOGGLE_PRS_ONOFF	()		{ return al175_do(0x81, 0x80			Z3);	}
CANCEL_BOOST()1057 ACT	CANCEL_BOOST		()		{ return al175_do(0x82, 0x80			Z3);	}
STOP_BATTERY_TEST()1058 ACT	STOP_BATTERY_TEST	()		{ return al175_do(0x83, 0x80			Z3);	}
START_BATTERY_TEST(VV_t EndVolt,mm_t Minutes)1059 ACT	START_BATTERY_TEST	(VV_t EndVolt, mm_t Minutes)
1060 						{ return al175_do(0x83, 0x81, EndVolt, Minutes	Z1);	}
1061 
SET_FLOAT_VOLTAGE(VV_t v)1062 ACT	SET_FLOAT_VOLTAGE	(VV_t v)	{ return al175_do(0x87, 0x80, v			Z2);	}
SET_BOOST_VOLTAGE(VV_t v)1063 ACT	SET_BOOST_VOLTAGE	(VV_t v)	{ return al175_do(0x87, 0x81, v			Z2);	}
SET_HIGH_BATTERY_LIMIT(VV_t Vhigh)1064 ACT	SET_HIGH_BATTERY_LIMIT	(VV_t Vhigh)	{ return al175_do(0x87, 0x82, Vhigh		Z2);	}
SET_LOW_BATTERY_LIMIT(VV_t Vlow)1065 ACT	SET_LOW_BATTERY_LIMIT	(VV_t Vlow)	{ return al175_do(0x87, 0x83, Vlow		Z2);	}
1066 
SET_DISCONNECT_LEVEL_AND_DELAY(VV_t level,mm_t delay)1067 ACT	SET_DISCONNECT_LEVEL_AND_DELAY
1068 				(VV_t level, mm_t delay)
1069 						{ return al175_do(0x87, 0x84, level, delay	Z1);	}
1070 
RESET_ALARMS()1071 ACT	RESET_ALARMS		()		{ return al175_do(0x88, 0x80			Z3);	}
CHANGE_COMM_PROTOCOL()1072 ACT	CHANGE_COMM_PROTOCOL	()		{ return al175_do(0x89, 0x80			Z3);	}
SET_VOLTAGE_AT_ZERO_T(VV_t v)1073 ACT	SET_VOLTAGE_AT_ZERO_T	(VV_t v)	{ return al175_do(0x8a, 0x80, v			Z2);	}
SET_SLOPE_AT_ZERO_T(VV_t mv_per_degree)1074 ACT	SET_SLOPE_AT_ZERO_T	(VV_t mv_per_degree)
1075 						{ return al175_do(0x8a, 0x81, mv_per_degree	Z2);	}
1076 
SET_MAX_TCOMP_VOLTAGE(VV_t v)1077 ACT	SET_MAX_TCOMP_VOLTAGE	(VV_t v)	{ return al175_do(0x8a, 0x82, v			Z2);	}
SET_MIN_TCOMP_VOLTAGE(VV_t v)1078 ACT	SET_MIN_TCOMP_VOLTAGE	(VV_t v)	{ return al175_do(0x8a, 0x83, v			Z2);	}
SWITCH_TEMP_COMP(uint16_t on)1079 ACT	SWITCH_TEMP_COMP	(uint16_t on)	{ return al175_do(0x8b, 0x80, on		Z2);	}
1080 
SWITCH_SYM_ALARM()1081 ACT	SWITCH_SYM_ALARM	()		{ return al175_do(0x8c, 0x80			Z3);	}
1082 
1083 
1084 /**
1085  * extract double value from a word
1086  */
d16(byte_t data[2])1087 static double d16(byte_t data[2])
1088 {
1089 	return (data[1] + 0x100*data[0]) / 100.0;
1090 }
1091 
upsdrv_updateinfo(void)1092 void upsdrv_updateinfo(void)
1093 {
1094 	/* int flags; */
1095 
1096 	byte_t x4000[9];		/* registers from 0x4000 to 0x4040 inclusive */
1097 	byte_t x4048[2];		/* 0x4048 - 0x4050	*/
1098 	byte_t x4100[8];		/* 0x4100 - 0x4138	*/
1099 	byte_t x4180[8];		/* 0x4180 - 0x41b8	*/
1100 	byte_t x4300[2];		/* 0x4300 - 0x4308	*/
1101 	int err;
1102 
1103 	double batt_current = 0.0;
1104 
1105 
1106 	upsdebugx(4, " ");
1107 	upsdebugx(4, "UPDATEINFO");
1108 	upsdebugx(4, "----------");
1109 	io_new_transaction(/*timeout=*/3);
1110 
1111 #define	RECV(reg) do { \
1112 	err = al175_read(x ## reg, 0x ## reg, sizeof(x ## reg));	\
1113 	if (err==-1) {							\
1114 		dstate_datastale();					\
1115 		return;							\
1116 	}								\
1117 } while (0)
1118 
1119 	RECV(4000);
1120 	RECV(4048);
1121 	RECV(4100);
1122 	RECV(4180);
1123 	RECV(4300);
1124 
1125 
1126 	status_init();
1127 
1128 	/* XXX non conformant with NUT naming & not well understood what they mean */
1129 #if 0
1130 				/* 0x4000   DIGITAL INPUT 1-8	*/
1131 	dstate_setinfo("load.fuse",		(x4000[0] & 0x80) ? "OK" : "BLOWN");
1132 	dstate_setinfo("battery.fuse",		(x4000[0] & 0x40) ? "OK" : "BLOWN");
1133 	dstate_setinfo("symalarm.fuse",		(x4000[0] & 0x20) ? "OK" : "BLOWN");
1134 
1135 				/* 0x4008   BATTERY INFORMATION	*/
1136 	dstate_setinfo("battery.contactor",	(x4000[1] & 0x80) ? "XX" : "YY");	/* FIXME */
1137 	dstate_setinfo("load.contactor",	(x4000[1] & 0x40) ? "XX" : "YY");	/* FIXME */
1138 	dstate_setinfo("lvd.contactor",		(x4000[1] & 0x20) ? "XX" : "YY");	/* FIXME */
1139 #endif
1140 	if (x4000[0] & 0x40){
1141 		dstate_setinfo("battery.fuse",	"FAIL");
1142 		status_set("RB");
1143 	}else{
1144 		dstate_setinfo("battery.fuse",	"OK");
1145 	}
1146 
1147 	if (x4000[0] & 0x20){
1148 		dstate_setinfo("battery.symmetry",	"FAIL");
1149 		status_set("RB");
1150 	}else{
1151 		dstate_setinfo("battery.symmetry",	"OK");
1152 	}
1153 
1154 	if (x4000[1] & 0x01)	/* battery test running	*/
1155 		status_set("TEST");
1156 
1157 	/* TODO: others from 0x4008 */
1158 
1159 				/* 0x4010   NOT USED	*/
1160 				/* 0x4018   NOT USED	*/
1161 
1162 	switch (x4000[4]) {	/* 0x4020   MAINS VOLTAGE STATUS	*/
1163 		case 0:     status_set("OL");   break;
1164 		case 1:     status_set("OB");   break;
1165 
1166 		case 2:     /* doc: "not applicable" */
1167 		default:
1168 			upsdebugx(2, "%s: invalid mains voltage status\t(%i)", __func__, x4000[4]);
1169 	}
1170 
1171 				/* 0x4028   SYSTEM ON OFF STATUS	*/
1172 	switch (x4000[5]) {
1173 		case 0: /* system on */     break;
1174 		case 1:	status_set("OFF");  break;
1175 
1176 		default:
1177 			upsdebugx(2, "%s: invalid system on/off status\t(%i)", __func__, x4000[5]);
1178 	}
1179 
1180 	switch (x4000[6]) {	/* 0x4030   BATTERY TEST FAIL	*/
1181 		case 0:     dstate_setinfo("ups.test.result", "OK");
1182 			break;
1183 
1184 		case 1:     status_set("RB");
1185 			dstate_setinfo("ups.test.result", "FAIL");
1186 			break;
1187 
1188 		default:
1189 			upsdebugx(2, "%s: invalid battery test fail\t(%i)", __func__, x4000[6]);
1190 	}
1191 	switch (x4000[7]) {	/* 0x4038   BATTERY VOLTAGE STATUS	*/
1192 		case 0:     /* normal */	break;
1193 		case 1:     status_set("LB");	break;
1194 		case 2:     status_set("HB");   break;
1195 
1196 		default:
1197 			upsdebugx(2, "%s: invalid battery voltage status\t(%i)", __func__, x4000[7]);
1198 	}
1199 	switch (x4000[8]) {	/* 0x4040   POS./NEG. BATT. CURRENT	*/
1200 		case 0:	batt_current = +1.0;	break;	/* positive	*/
1201 		case 1:	batt_current = -1.0;	break;	/* negative	*/
1202 
1203 		default:
1204 			upsdebugx(2, "%s: invalid pos/neg battery current\t(%i)", __func__, x4000[8]);
1205 	}
1206 
1207 	switch (x4048[0]) {	/* 0x4048   BOOST STATUS	*/
1208 		case 0:	/* no boost */;		break;
1209 		case 1:	status_set("BOOST");	break;
1210 
1211 		default:
1212 			upsdebugx(2, "%s: invalid boost status\t(%i)", __func__, x4048[0]);
1213 	}
1214 
1215 	{
1216 		const char *v=NULL;
1217 
1218 		switch (x4048[1]) {	/* 0x4050   SYSTEM VOLTAGE STAT.	*/
1219 			case 0:	v = "48";	break;
1220 			case 1: v = "24";	break;
1221 			case 2: v = "12";	break;
1222 			case 3: v = "26";	break;
1223 			case 4: v = "60";	break;
1224 
1225 			default:
1226 				upsdebugx(2, "%s: invalid system voltage status\t(%i)", __func__, x4048[1]);
1227 		}
1228 
1229 		if (v)
1230 			dstate_setinfo("output.voltage.nominal",    "%s", v);
1231 	}
1232 
1233 
1234 				/* 0x4100   BATTERY VOLTAGE REF	*/
1235 	dstate_setinfo("battery.voltage.nominal",	"%.2f", d16(x4100+0));
1236 
1237 				/* 0x4110   BOOST VOLTAGE REF	*/
1238 	dstate_setinfo("input.transfer.boost.low",	"%.2f", d16(x4100+2));	/* XXX: boost.high ?	*/
1239 
1240 				/* 0x4120   HIGH BATT VOLT REF		XXX	*/
1241 				/* 0x4130   LOW  BATT VOLT REF		XXX	*/
1242 
1243 				/* 0x4180   FLOAT VOLTAGE		XXX	*/
1244 				/* 0x4190   BATT CURRENT			*/
1245 	batt_current *= d16(x4180+2);
1246 	dstate_setinfo("battery.current",		"%.2f", batt_current);
1247 
1248 				/* 0x41b0   LOAD CURRENT (output.current in NUT)	*/
1249 	dstate_setinfo("output.current",		"%.2f", d16(x4180+6));
1250 
1251 				/* 0x4300   BATTERY TEMPERATURE	*/
1252 	dstate_setinfo("battery.temperature",		"%.2f", d16(x4300+0));
1253 
1254 
1255 	status_commit();
1256 
1257 	upsdebugx(1, "STATUS: %s", dstate_getinfo("ups.status"));
1258 	dstate_dataok();
1259 
1260 
1261 	/* out: */
1262 	return;
1263 
1264 }
1265 
1266 void upsdrv_shutdown(void)
1267 	__attribute__((noreturn));
1268 
upsdrv_shutdown(void)1269 void upsdrv_shutdown(void)
1270 {
1271 	/* TODO use TOGGLE_PRS_ONOFF for shutdown */
1272 
1273 	/* tell the UPS to shut down, then return - DO NOT SLEEP HERE */
1274 
1275 	/* maybe try to detect the UPS here, but try a shutdown even if
1276 	   it doesn't respond at first if possible */
1277 
1278 	/* replace with a proper shutdown function */
1279 	fatalx(EXIT_FAILURE, "shutdown not supported");
1280 
1281 	/* you may have to check the line status since the commands
1282 	   for toggling power are frequently different for OL vs. OB */
1283 
1284 	/* OL: this must power cycle the load if possible */
1285 
1286 	/* OB: the load must remain off until the power returns */
1287 }
1288 
1289 
instcmd(const char * cmdname,const char * extra)1290 static int instcmd(const char *cmdname, const char *extra)
1291 {
1292 	int err;
1293 
1294 	upsdebugx(1, "INSTCMD: %s", cmdname);
1295 
1296 	io_new_transaction(/*timeout=*/5);
1297 
1298 	/*
1299 	 * test.battery.start
1300 	 * test.battery.stop
1301 	 */
1302 
1303 	if (!strcasecmp(cmdname, "test.battery.start")) {
1304 		err = START_BATTERY_TEST(24, 1);
1305 		return (!err ? STAT_INSTCMD_HANDLED : STAT_INSTCMD_FAILED);
1306 	}
1307 
1308 	if (!strcasecmp(cmdname, "test.battery.stop")) {
1309 		err = STOP_BATTERY_TEST();
1310 		return (!err ? STAT_INSTCMD_HANDLED : STAT_INSTCMD_FAILED);
1311 	}
1312 
1313 	upslogx(LOG_NOTICE, "instcmd: unknown command [%s] [%s]", cmdname, extra);
1314 	return STAT_INSTCMD_UNKNOWN;
1315 }
1316 
1317 /* no help */
upsdrv_help(void)1318 void upsdrv_help(void)
1319 {
1320 }
1321 
1322 /* no -x flags */
upsdrv_makevartable(void)1323 void upsdrv_makevartable(void)
1324 {
1325 }
1326 
upsdrv_initups(void)1327 void upsdrv_initups(void)
1328 {
1329 	upsfd = ser_open(device_path);
1330 	ser_set_speed(upsfd, device_path, B9600);
1331 
1332 	ser_disable_flow_control();
1333 }
1334 
upsdrv_cleanup(void)1335 void upsdrv_cleanup(void)
1336 {
1337 	ser_close(upsfd, device_path);
1338 }
1339 
1340 
upsdrv_initinfo(void)1341 void upsdrv_initinfo(void)
1342 {
1343 	/* TODO issue short io with UPS to detect it's presence */
1344 	/* try to detect the UPS here - call fatal_with_errno(EXIT_FAILURE, ) if it fails */
1345 
1346 	dstate_setinfo("ups.mfr", "Eltek");
1347 	dstate_setinfo("ups.model", "AL175");
1348 	/* ... */
1349 
1350 	/* instant commands */
1351 	dstate_addcmd ("test.battery.start");
1352 	dstate_addcmd ("test.battery.stop");
1353 	/* TODO rest instcmd(s) */
1354 
1355 	upsh.instcmd = instcmd;
1356 }
1357