1 /*
2 *
3 * microdowell.c: support for Microdowell Enterprise Nxx/Bxx serial protocol based UPSes
4 *
5 * Copyright (C) Elio Corbolante <eliocor at microdowell.com>
6 *
7 * microdowell.c created on 27/09/2007
8 *
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2 of the License, or
12 * (at your option) any later version.
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
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; if not, write to the Free Software
21 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
22 */
23
24 /*
25 anything commented is optional
26 anything else is mandatory
27 */
28
29 #include "main.h"
30 #include "serial.h"
31 #include <sys/ioctl.h>
32 #include "timehead.h"
33 #include "nut_stdint.h"
34
35 #define ENTERPRISE_PROTOCOL
36 #include "microdowell.h"
37
38 #define MAX_START_DELAY 999999
39 #define MAX_SHUTDOWN_DELAY 32767
40 /* Maximum length of a string representing these values */
41 #define MAX_START_DELAY_LEN 6
42 #define MAX_SHUTDOWN_DELAY_LEN 5
43
44 #define DRIVER_NAME "MICRODOWELL UPS driver"
45 #define DRIVER_VERSION "0.01"
46
47 /* driver description structure */
48 upsdrv_info_t upsdrv_info = {
49 DRIVER_NAME,
50 DRIVER_VERSION,
51 "Elio Corbolante <eliocor@microdowell.com>",
52 DRV_STABLE,
53 { NULL }
54 };
55
56 static ENT_STRUCT ups ;
57
58 /* common driver routines */
59 int instcmd(const char *cmdname, const char *extra);
60 int setvar(const char *varname, const char *val);
61
62 /* he knew... macros should evaluate their arguments only once */
63 #define CLAMP(x, min, max) (((x) < (min)) ? (min) : (((x) > (max)) ? (max) : (x)))
64
CheckDataChecksum(unsigned char * Buff,size_t Len)65 static int CheckDataChecksum(unsigned char *Buff, size_t Len)
66 {
67 size_t i, Idx ;
68 unsigned char Xor ;
69
70 ups.FramePointer = Xor = 0 ;
71 for (Idx=0 ; Idx < Len ; Idx++)
72 if (Buff[Idx] == STX_CHAR)
73 break ;
74
75 ups.FramePointer = Idx ; /* Memorise start point. */
76
77 /* Check that the message is not to short... */
78 if ( (Idx > (Len-4)) || (Idx+Buff[Idx+1]+2 > Len) )
79 return(ERR_MSG_TOO_SHORT) ; /* Too short a message! */
80
81 /* Calculate checksum */
82 for (i=Idx+1 ; i < Idx+Buff[Idx+1]+2 ; i++)
83 Xor ^= Buff[i] ;
84
85 /* if Xor != then checksum error */
86 if (Xor != Buff[i])
87 return(ERR_MSG_CHECKSUM) ; /* error in checksum */
88
89 /* If checksum OK: return */
90 return(0) ;
91 }
92
93
94 static const char *ErrMessages[] = {
95 /* 0 */ "errorcode NOT DEFINED", /* default error message */
96 /* 1 */ "I2C bus busy (e2prom)",
97 /* 2 */ "Command received: checksum not valid",
98 /* 3 */ "Command received: unrecognized command",
99 /* 4 */ "WRITE: eeprom address not multiple of 8",
100 /* 5 */ "READ: eeprom address (added with size) out of bound ",
101 /* 6 */ "error writing e2prom address",
102 /* 7 */ "error writing e2prom subaddress",
103 /* 8 */ "error reading e2prom data",
104 /* 9 */ "error writing e2prom address",
105 /* 10 */ "error reading e2prom subaddress",
106 /* 11 */ "error writing e2prom data",
107 /* 12 */ "error writing e2prom address during data verification",
108 /* 13 */ "error verification e2prom data",
109 /* 14 */ "e2prom data are different from those in the write buffer",
110 /* 15 */ "e2prom checksum error",
111
112 /* 16 */ "NO CHARS FROM PORT",
113 /* 17 */ "TOO FEW DATA RECEIVED: [STX] near end of message",
114 /* 18 */ "CHECKSUM ERROR IN MESSAGE",
115 /* 19 */ "OK",
116 /* */ ""
117 } ;
118
PrintErr(int ErrCode)119 static const char *PrintErr(int ErrCode)
120 {
121 int msgIndex = 0 ;
122
123 /* The default 'msgIndex' is 0 (error code not defined) */
124 switch (ErrCode) {
125 case ERR_NO_ERROR : msgIndex = 19 ; break ;
126
127 case ERR_I2C_BUSY : msgIndex = 1 ; break ;
128 case ERR_CMD_CHECKSUM : msgIndex = 2 ; break ;
129 case ERR_CMD_UNRECOG : msgIndex = 3 ; break ;
130 case ERR_EEP_NOBLOCK : msgIndex = 4 ; break ;
131 case ERR_EEP_OOBOUND : msgIndex = 5 ; break ;
132 case ERR_EEP_WADDR1 : msgIndex = 6 ; break ;
133 case ERR_EEP_WSADDR1 : msgIndex = 7 ; break ;
134 case ERR_EEP_RDATA : msgIndex = 8 ; break ;
135 case ERR_EEP_WADDR2 : msgIndex = 9 ; break ;
136 case ERR_EEP_WSADDR2 : msgIndex = 10 ; break ;
137 case ERR_EEP_WDATA : msgIndex = 11 ; break ;
138 case ERR_EEP_WADDRVER : msgIndex = 12 ; break ;
139 case ERR_EEP_WDATAVER : msgIndex = 13 ; break ;
140 case ERR_EEP_VERIFY : msgIndex = 14 ; break ;
141 case ERR_EEP_CHECKSUM : msgIndex = 15 ; break ;
142
143 case ERR_COM_NO_CHARS : msgIndex = 16 ; break ;
144 case ERR_MSG_TOO_SHORT : msgIndex = 17 ; break ;
145 case ERR_MSG_CHECKSUM : msgIndex = 18 ; break ;
146
147 default : msgIndex = 0 ; break ;
148 }
149 return(ErrMessages[msgIndex]) ;
150 }
151
CheckErrCode(unsigned char * Buff)152 static int CheckErrCode(unsigned char * Buff)
153 {
154 auto int Ret ;
155
156 switch (Buff[2]) {
157 /* I have found an error */
158 case CMD_NACK :
159 Ret = Buff[3] ;
160 break ;
161
162 case CMD_ACK :
163 case CMD_GET_STATUS :
164 case CMD_GET_MEASURES :
165 case CMD_GET_CONFIG :
166 case CMD_GET_BATT_STAT :
167 case CMD_GET_MASK :
168 case CMD_SET_TIMER :
169 case CMD_BATT_TEST :
170 case CMD_GET_BATT_TEST :
171 case CMD_SD_ONESHOT :
172 case CMD_GET_SD_ONESHOT:
173 case CMD_SET_SCHEDULE :
174 case CMD_GET_SCHEDULE :
175 case CMD_GET_EEP_BLOCK :
176 case CMD_SET_EEP_BLOCK :
177 case CMD_GET_EEP_SEED :
178 case CMD_INIT :
179 Ret = 0 ;
180 break ;
181
182 /* command not recognized */
183 default:
184 Ret = ERR_CMD_UNRECOG ;
185 break ;
186 }
187 return(Ret) ;
188 }
189
190
SendCmdToSerial(unsigned char * Buff,size_t Len)191 static void SendCmdToSerial(unsigned char *Buff, size_t Len)
192 {
193 int i;
194 unsigned char Tmp[20], Xor ;
195
196 Tmp[0] = STX_CHAR ;
197 Xor = Tmp[1] = (unsigned char) (Len & 0x1f) ;
198 for (i=0 ; i < Tmp[1] ; i++)
199 {
200 Tmp[i+2] = Buff[i] ;
201 Xor ^= Buff[i] ;
202 }
203 Tmp[Len+2] = Xor ;
204
205 upsdebug_hex(4, "->UPS", Tmp, Len+3) ;
206
207 /* flush serial port */
208 ser_flush_in(upsfd, "", 0) ; /* empty input buffer */
209 ser_send_buf(upsfd, Tmp, Len+3) ; /* send data to the UPS */
210 }
211
CmdSerial(unsigned char * OutBuffer,size_t Len,unsigned char * RetBuffer)212 static unsigned char * CmdSerial(unsigned char *OutBuffer, size_t Len, unsigned char *RetBuffer)
213 {
214 #define TMP_BUFF_LEN 1024
215 unsigned char InpBuff[TMP_BUFF_LEN+1] ;
216 unsigned char TmpBuff[3] ;
217 int i, ErrCode ;
218 unsigned char *p ;
219 size_t BuffLen ;
220
221 /* The default error code (no received character) */
222 ErrCode = ERR_COM_NO_CHARS ;
223
224 SendCmdToSerial(OutBuffer, Len) ;
225 usleep(10000) ; /* small delay (1/100 s) */
226
227 /* get chars until timeout */
228 BuffLen = 0 ;
229 while (ser_get_char(upsfd, TmpBuff, 0, 10000) == 1)
230 {
231 InpBuff[BuffLen++] = TmpBuff[0] ;
232 if (BuffLen > TMP_BUFF_LEN)
233 break ;
234 }
235
236 upsdebug_hex(4, "UPS->", InpBuff, BuffLen) ;
237
238 if (BuffLen > 0)
239 {
240 ErrCode = CheckDataChecksum(InpBuff, BuffLen) ;
241 /* upsdebugx(4, "ErrCode = %d / Len = %d", ErrCode, BuffLen); */
242
243 if (!ErrCode)
244 {
245 /* FramePointer to valid data! */
246 p = InpBuff + ups.FramePointer ;
247 /* p now point to valid data.
248 check if it is a error code. */
249 ErrCode = CheckErrCode(p) ;
250 if (!ErrCode)
251 {
252 /* I copy the data read in the buffer */
253 for(i=0 ; i<(int) (p[1])+3 ; i++)
254 RetBuffer[i] = p[i] ;
255 ups.ErrCode = ups.ErrCount = ups.CommStatus = 0 ;
256 return(RetBuffer) ;
257 }
258 }
259 }
260
261 /* if they have arrived here, wants to say that I have found an error.... */
262 ups.ErrCode = ErrCode ;
263 ups.ErrCount++ ;
264 if (ups.ErrCount > 3)
265 {
266 ups.CommStatus &= 0x80 ;
267 ups.CommStatus |= (unsigned char) (ups.ErrCount & 0x7F) ;
268 if (ups.ErrCount > 100)
269 ups.ErrCount = 100 ;
270 }
271 return(NULL) ; /* There have been errors in the reading of the data */
272 }
273
detect_hardware(void)274 static int detect_hardware(void)
275 {
276 unsigned char OutBuff[20] ;
277 unsigned char InpBuff[260] ;
278 unsigned char *p ;
279 int i, retries ;
280 struct tm *Time ;
281 struct tm tmbuf;
282 time_t lTime ;
283
284 ups.ge_2kVA = 0 ;
285
286 for (retries=0 ; retries <= 4 ; retries++)
287 {
288 /* Identify UPS model */
289 OutBuff[0] = CMD_GET_EEP_BLOCK ; /* get EEPROM data */
290 OutBuff[1] = EEP_UPS_MODEL ; /* UPS model */
291 OutBuff[2] = 8 ; /* number of bytes */
292 if ((p = CmdSerial(OutBuff, LEN_GET_EEP_BLOCK, InpBuff)) != NULL)
293 {
294 /* got UPS model */
295 for (i=0 ; i<8 ; i++)
296 ups.UpsModel[i] = (char)p[i+5] ;
297 ups.UpsModel[8] = '\0' ;
298 upsdebugx(2, "get 'UPS model': %s", PrintErr(ups.ErrCode));
299 break ; /* UPS identified: exit from ' for' LOOP */
300 }
301 else
302 {
303 upsdebugx(1, "[%d] get 'UPS model': %s", retries, PrintErr(ups.ErrCode));
304 upslogx(LOG_ERR, "[%d] Unable to identify UPS model [%s]", retries, PrintErr(ups.ErrCode));
305 usleep(100000) ; /* small delay (1/10 s) for next retry */
306 }
307 }
308
309 /* check if I was unable to find the UPS */
310 if (retries == 4) /* UPS not found! */
311 return -1;
312
313 /* UPS serial number */
314 OutBuff[0] = CMD_GET_EEP_BLOCK ; /* get EEPROM data */
315 OutBuff[1] = EEP_SERIAL_NUM ; /* UPS serial # */
316 OutBuff[2] = 8 ; /* number of bytes */
317 if ((p = CmdSerial(OutBuff, LEN_GET_EEP_BLOCK, InpBuff)) != NULL)
318 {
319 /* got UPS serial # */
320 for (i=0 ; i<8 ; i++)
321 ups.SerialNumber[i] = (char)p[i+5] ;
322 ups.SerialNumber[8] = '\0' ;
323 upsdebugx(2, "get 'UPS Serial #': %s", PrintErr(ups.ErrCode));
324 }
325 else
326 {
327 upsdebugx(1, "get 'UPS Serial #': %s", PrintErr(ups.ErrCode));
328 upslogx(LOG_ERR, "Unable to identify UPS serial # [%s]", PrintErr(ups.ErrCode));
329 return -1;
330 }
331
332
333 /* Get Production date & FW info */
334 OutBuff[0] = CMD_GET_EEP_BLOCK ; /* get EEPROM data */
335 OutBuff[1] = EEP_PROD_DATE ; /* Production date + HW version */
336 OutBuff[2] = 8 ; /* number of bytes */
337 if ((p = CmdSerial(OutBuff, LEN_GET_EEP_BLOCK, InpBuff)) != NULL)
338 {
339 /* got Production date & FW info */
340 p += 5 ; /* 'p' points to eeprom data */
341 ups.YearOfProd = 2000 + p[0] ; /* Production year of the UPS */
342 ups.MonthOfProd = p[1] ; /* Production month of the UPS */
343 ups.DayOfProd = p[2] ; /* Production day of the UPS */
344 ups.HW_MajorVersion = (p[3]>>4) & 0x0F ; /* Hardware: Major version */
345 ups.HW_MinorVersion = (p[3] & 0x0F) ; /* Hardware: Minor version */
346 ups.BR_MajorVersion = (p[4]>>4) & 0x0F ; /* BoardHardware: Major version */
347 ups.BR_MinorVersion = (p[4] & 0x0F) ; /* BoardHardware: Minor version */
348 ups.FW_MajorVersion = (p[5]>>4) & 0x0F ; /* Firmware: Major version */
349 ups.FW_MinorVersion = (p[5] & 0x0F) ; /* Firmware: Minor version */
350 ups.FW_SubVersion = p[6] ; /* Firmware: SUBVERSION (special releases */
351 ups.BatteryNumber = p[7] ; /* number of batteries in UPS */
352 upsdebugx(2, "get 'Production date': %s", PrintErr(ups.ErrCode));
353 }
354 else
355 {
356 upsdebugx(1, "get 'Production date': %s", PrintErr(ups.ErrCode));
357 upslogx(LOG_ERR, "Unable to read Production date [%s]", PrintErr(ups.ErrCode));
358 return -1;
359 }
360
361
362 /* Get Battery substitution date */
363 OutBuff[0] = CMD_GET_EEP_BLOCK ; /* get EEPROM data */
364 OutBuff[1] = EEP_BATT_SUBST ; /* Battery substitution dates */
365 OutBuff[2] = 8 ; /* number of bytes */
366 if ((p = CmdSerial(OutBuff, LEN_GET_EEP_BLOCK, InpBuff)) != NULL)
367 {
368 /* got Battery substitution date */
369 p += 5 ; /* 'p' points to eeprom data */
370 upsdebugx(2, "get 'Battery Subst. Dates': %s", PrintErr(ups.ErrCode));
371 }
372 else
373 {
374 upsdebugx(1, "get 'Battery Subst. Dates': %s", PrintErr(ups.ErrCode));
375 upslogx(LOG_ERR, "Unable to read Battery Subst. Dates [%s]", PrintErr(ups.ErrCode));
376 return -1;
377 }
378
379
380 /* Get working time (battery+normal)) */
381 OutBuff[0] = CMD_GET_EEP_BLOCK ; /* get EEPROM data */
382 OutBuff[1] = EEP_MIN_VBATT ; /* working time */
383 OutBuff[2] = 8 ; /* number of bytes */
384 if ((p = CmdSerial(OutBuff, LEN_GET_EEP_BLOCK, InpBuff)) != NULL)
385 {
386 /* got working time (battery+normal)) */
387 p += 5 ; /* 'p' points to eeprom data */
388 upsdebugx(2, "get 'UPS life info': %s", PrintErr(ups.ErrCode));
389 }
390 else
391 {
392 upsdebugx(1, "get 'UPS life info': %s", PrintErr(ups.ErrCode));
393 upslogx(LOG_ERR, "Unable to read UPS life info [%s]", PrintErr(ups.ErrCode));
394 return -1;
395 }
396
397
398 /* Get the THRESHOLD table (0) */
399 OutBuff[0] = CMD_GET_EEP_BLOCK ; /* get EEPROM data */
400 OutBuff[1] = EEP_THRESHOLD_0 ; /* Thresholds table 0 */
401 OutBuff[2] = 8 ; /* number of bytes */
402 if ((p = CmdSerial(OutBuff, LEN_GET_EEP_BLOCK, InpBuff)) != NULL)
403 {
404 /* got the THRESHOLD table (0) */
405 p += 5 ; /* 'p' points to eeprom data */
406 upsdebugx(2, "get 'Thresholds table 0': %s", PrintErr(ups.ErrCode));
407 }
408 else
409 {
410 upsdebugx(1, "get 'Thresholds table 0': %s", PrintErr(ups.ErrCode));
411 upslogx(LOG_ERR, "Unable to read Thresholds table 0 [%s]", PrintErr(ups.ErrCode));
412 return -1;
413 }
414
415
416 /* Get the THRESHOLD table (1) */
417 OutBuff[0] = CMD_GET_EEP_BLOCK ; /* get EEPROM data */
418 OutBuff[1] = EEP_THRESHOLD_1 ; /* Thresholds table 0 */
419 OutBuff[2] = 8 ; /* number of bytes */
420 if ((p = CmdSerial(OutBuff, LEN_GET_EEP_BLOCK, InpBuff)) != NULL)
421 {
422 /* got the THRESHOLD table (1) */
423 p += 5 ; /* 'p' points to eeprom data */
424 upsdebugx(2, "get 'Thresholds table 1': %s", PrintErr(ups.ErrCode));
425 }
426 else
427 {
428 upsdebugx(1, "get 'Thresholds table 1': %s", PrintErr(ups.ErrCode));
429 upslogx(LOG_ERR, "Unable to read Thresholds table 1 [%s]", PrintErr(ups.ErrCode));
430 return -1;
431 }
432
433
434 /* Get the THRESHOLD table (2) */
435 OutBuff[0] = CMD_GET_EEP_BLOCK ; /* get EEPROM data */
436 OutBuff[1] = EEP_THRESHOLD_2 ; /* Thresholds table 0 */
437 OutBuff[2] = 8 ; /* number of bytes */
438 if ((p = CmdSerial(OutBuff, LEN_GET_EEP_BLOCK, InpBuff)) != NULL)
439 {
440 /* got the THRESHOLD table (2) */
441 p += 5 ; /* 'p' points to eeprom data */
442 upsdebugx(2, "get 'Thresholds table 2': %s", PrintErr(ups.ErrCode));
443 }
444 else
445 {
446 upsdebugx(1, "get 'Thresholds table 2': %s", PrintErr(ups.ErrCode));
447 upslogx(LOG_ERR, "Unable to read Thresholds table 2 [%s]", PrintErr(ups.ErrCode));
448 return -1;
449 }
450
451
452 /* Get Option Bytes */
453 OutBuff[0] = CMD_GET_EEP_BLOCK ; /* get EEPROM data */
454 OutBuff[1] = EEP_OPT_BYTE_BLK ; /* Option Bytes */
455 OutBuff[2] = 8 ; /* number of bytes */
456 if ((p = CmdSerial(OutBuff, LEN_GET_EEP_BLOCK, InpBuff)) != NULL)
457 {
458 /* got Option Bytes */
459 p += 5 ; /* 'p' points to eeprom data */
460 dstate_setinfo("input.voltage.nominal", "%s", (p[EEP_OPT_BYTE_1] & 0x02) ? "110": "230") ;
461 dstate_setinfo("input.frequency", "%s", (p[EEP_OPT_BYTE_1] & 0x01) ? "60.0": "50.0") ;
462 upsdebugx(2, "get 'Option Bytes': %s", PrintErr(ups.ErrCode));
463 }
464 else
465 {
466 upsdebugx(1, "get 'Option Bytes': %s", PrintErr(ups.ErrCode));
467 upslogx(LOG_ERR, "Unable to read Option Bytes [%s]", PrintErr(ups.ErrCode));
468 return -1;
469 }
470
471
472 /* Get UPS sensitivity (fault points) */
473 OutBuff[0] = CMD_GET_EEP_BLOCK ; /* get EEPROM data */
474 OutBuff[1] = EEP_FAULT_POINTS ; /* Number of fault points (sensitivity)) */
475 OutBuff[2] = 8 ; /* number of bytes */
476 if ((p = CmdSerial(OutBuff, LEN_GET_EEP_BLOCK, InpBuff)) != NULL)
477 {
478 /* got UPS sensitivity (fault points) */
479 p += 5 ; /* 'p' points to eeprom data */
480 switch (p[0]) {
481 case 1 : dstate_setinfo("input.sensitivity", "H") ; break ;
482 case 2 : dstate_setinfo("input.sensitivity", "M") ; break ;
483 case 3 : dstate_setinfo("input.sensitivity", "L") ; break ;
484 default : dstate_setinfo("input.sensitivity", "L") ; break ;
485 }
486 upsdebugx(2, "get 'Input Sensitivity': %s", PrintErr(ups.ErrCode));
487 }
488 else
489 {
490 upsdebugx(1, "get 'Input Sensitivity': %s", PrintErr(ups.ErrCode));
491 upslogx(LOG_ERR, "Unable to read Input Sensitivity [%s]", PrintErr(ups.ErrCode));
492 return -1;
493 }
494
495
496 /* Set internal UPS clock */
497 time(&lTime) ;
498 Time = localtime_r(&lTime, &tmbuf);
499
500 OutBuff[0] = CMD_SET_TIMER ; /* set UPS internal timer */
501 OutBuff[1] = (Time->tm_wday+6) % 7 ; /* week day (0=monday) */
502 OutBuff[2] = (unsigned char)Time->tm_hour ; /* hours */
503 OutBuff[3] = (unsigned char)Time->tm_min ; /* minutes */
504 OutBuff[4] = (unsigned char)Time->tm_sec ; /* seconds */
505 if ((p = CmdSerial(OutBuff, LEN_SET_TIMER, InpBuff)) != NULL)
506 {
507 upsdebugx(2, "set 'UPS internal clock': %s", PrintErr(ups.ErrCode));
508 }
509 else
510 {
511 upsdebugx(1, "set 'UPS internal clock': %s", PrintErr(ups.ErrCode));
512 upslogx(LOG_ERR, "Unable to set UPS internal clock [%s]", PrintErr(ups.ErrCode));
513 return -1;
514 }
515
516 return 0; /* everything was OK */
517 }
518
519
520 /* ========================= */
521
522
upsdrv_updateinfo(void)523 void upsdrv_updateinfo(void)
524 {
525 unsigned char OutBuff[20] ;
526 unsigned char InpBuff[260] ;
527 unsigned char *p ;
528 /* int i ; */
529
530 OutBuff[0] = CMD_GET_STATUS ; /* get UPS status */
531 if ((p = CmdSerial(OutBuff, LEN_GET_STATUS, InpBuff)) != NULL)
532 {
533 p += 3 ; /* 'p' points to received data */
534
535 status_init(); /* reset status flags */
536
537 /* store last UPS status */
538 ups.StatusUPS = (uint32_t)p[0] ;
539 ups.StatusUPS |= ((uint32_t)p[1]<<8) ;
540 ups.StatusUPS |= ((uint32_t)p[2]<<16) ;
541 ups.StatusUPS |= ((uint32_t)p[3]<<24) ;
542 ups.ShortStatus = (uint16_t)p[0];
543 ups.ShortStatus |= ((uint16_t)p[1]<<8) ;
544 upsdebugx(1, "ups.StatusUPS: %08" PRIX32, ups.StatusUPS);
545 upsdebugx(1, "ups.ShortStatus: %04" PRIX16, ups.ShortStatus);
546
547 /* on battery? */
548 if (p[0] & 0x01)
549 status_set("OB"); /* YES */
550
551 /* LOW battery? */
552 if (p[0] & 0x02)
553 status_set("LB"); /* YES */
554
555 /* online? */
556 if (p[0] & 0x08)
557 status_set("OL"); /* YES */
558
559 /* Overload? */
560 if (p[1] & 0xC0)
561 status_set("OVER"); /* YES */
562
563 /* Offline/Init/Stanby/Waiting for mains? */
564 if (p[0] & 0xE0)
565 status_set("OFF"); /* YES */
566
567 /* AVR on (boost)? */
568 if (p[4] & 0x04)
569 status_set("BOOST"); /* YES */
570
571 /* AVR on (buck)? */
572 if (p[4] & 0x08)
573 status_set("TRIM"); /* YES */
574
575 dstate_setinfo("ups.time", "%02d:%02d:%02d", p[6], p[7], p[8]) ;
576 upsdebugx(3, "get 'Get Status': %s", PrintErr(ups.ErrCode));
577 }
578 else
579 {
580 upsdebugx(1, "get 'Get Status': %s", PrintErr(ups.ErrCode));
581 /* upslogx(LOG_ERR, "get 'Get Status': %s", PrintErr(ups.ErrCode)); */
582 dstate_datastale();
583 return;
584 }
585
586 /* ========================= */
587
588 OutBuff[0] = CMD_GET_MEASURES ; /* get UPS values */
589 if ((p = CmdSerial(OutBuff, LEN_GET_MEASURES, InpBuff)) != NULL)
590 {
591 p += 3 ; /* 'p' points to received data */
592
593 dstate_setinfo("input.voltage", "%d", (int)((float)(p[2]*256 + p[3]) / 36.4)) ;
594 if (ups.ge_2kVA)
595 {
596 dstate_setinfo("output.voltage", "%d", (int)((float)(p[6]*256 + p[7]) / 63.8)) ;
597 dstate_setinfo("output.current", "%1.f", ((float)(p[8]*256 + p[9]) / 635.0)) ;
598 dstate_setinfo("battery.voltage", "%.1f", ((float) (p[4]*256 + p[5])) / 329.0) ;
599 }
600 else
601 {
602 dstate_setinfo("output.voltage", "%d", (int)((float)(p[6]*256 + p[7]) / 36.4)) ;
603 dstate_setinfo("output.current", "%1.f", ((float)(p[8]*256 + p[9]) / 1350.0)) ;
604 dstate_setinfo("battery.voltage", "%.1f", ((float) (p[4]*256 + p[5])) / 585.0) ;
605 }
606
607 dstate_setinfo("ups.temperature", "%d", (int)(((float)(p[10]*256 + p[11])-202.97) / 1.424051)) ;
608 upsdebugx(3, "get 'Get Measures': %s", PrintErr(ups.ErrCode));
609 }
610 else
611 {
612 /* upsdebugx(1, "get 'Get Measures': %s", PrintErr(ups.ErrCode)); */
613 upslogx(LOG_ERR, "get 'Get Measures': %s", PrintErr(ups.ErrCode));
614 dstate_datastale();
615 return;
616 }
617
618 /* ========================= */
619
620 OutBuff[0] = CMD_GET_BAT_LD ; /* get UPS Battery and Load values */
621 if ((p = CmdSerial(OutBuff, LEN_GET_BAT_LD, InpBuff)) != NULL)
622 {
623 p += 3 ; /* 'p' points to received data */
624
625 dstate_setinfo("ups.power", "%d", (p[4]*256 + p[5])) ;
626 /* dstate_setinfo("ups.realpower", "%d", (int)((float)(p[4]*256 + p[5]) * 0.6)) ; */
627 dstate_setinfo("battery.charge", "%d", (int)p[0]) ;
628 dstate_setinfo("ups.load", "%d", (int)p[6]) ;
629 upsdebugx(3, "get 'Get Batt+Load Status': %s", PrintErr(ups.ErrCode));
630 }
631 else
632 {
633 /* upsdebugx(1, "get 'Get Batt+Load Status': %s", PrintErr(ups.ErrCode)); */
634 upslogx(LOG_ERR, "get 'Get Batt+Load Status': %s", PrintErr(ups.ErrCode));
635 dstate_datastale();
636 return;
637 }
638
639 status_commit();
640 dstate_dataok();
641
642 poll_interval = 2;
643 }
644
645
646 /* ========================= */
647
648
instcmd(const char * cmdname,const char * extra)649 int instcmd(const char *cmdname, const char *extra)
650 {
651 unsigned char OutBuff[20] ;
652 unsigned char InpBuff[260] ;
653 unsigned char *p ;
654 /* int i ; */
655
656 upsdebugx(1, "instcmd(%s, %s)", cmdname, extra);
657
658
659 if (strcasecmp(cmdname, "load.on") == 0)
660 {
661 OutBuff[0] = CMD_SD_ONESHOT ; /* turn ON the outputs */
662 OutBuff[1] = 0xFF ; /* ALL outputs */
663 OutBuff[2] = 0x08 ; /* Enable outputs (immediately) */
664 OutBuff[3] = 0 ;
665 OutBuff[4] = 0 ;
666 OutBuff[5] = 0 ;
667 OutBuff[6] = 0 ;
668 OutBuff[7] = 0 ;
669 if ((p = CmdSerial(OutBuff, LEN_SD_ONESHOT, InpBuff)) != NULL)
670 {
671 p += 3 ; /* 'p' points to received data */
672 upslogx(LOG_INFO, "Turning load on.");
673 upsdebugx(1, "'SdOneshot(turn_ON)': %s", PrintErr(ups.ErrCode));
674 }
675 else
676 {
677 upsdebugx(1, "'SdOneshot(turn_ON)': %s", PrintErr(ups.ErrCode));
678 upslogx(LOG_ERR, "'SdOneshot(turn_ON)': %s", PrintErr(ups.ErrCode));
679 }
680 return STAT_INSTCMD_HANDLED;
681 }
682
683 if (strcasecmp(cmdname, "load.off") == 0)
684 {
685 OutBuff[0] = CMD_SD_ONESHOT ; /* turn ON the outputs */
686 OutBuff[1] = 0xFF ; /* ALL outputs */
687 OutBuff[2] = 0x04 ; /* Disable outputs (immediately) */
688 OutBuff[3] = 0 ;
689 OutBuff[4] = 0 ;
690 OutBuff[5] = 0 ;
691 OutBuff[6] = 0 ;
692 OutBuff[7] = 0 ;
693 if ((p = CmdSerial(OutBuff, LEN_SD_ONESHOT, InpBuff)) != NULL)
694 {
695 p += 3 ; /* 'p' points to received data */
696 upslogx(LOG_INFO, "Turning load on.");
697 upsdebugx(1, "'SdOneshot(turn_OFF)': %s", PrintErr(ups.ErrCode));
698 }
699 else
700 {
701 upsdebugx(1, "'SdOneshot(turn_OFF)': %s", PrintErr(ups.ErrCode));
702 upslogx(LOG_ERR, "'SdOneshot(turn_OFF)': %s", PrintErr(ups.ErrCode));
703 }
704 return STAT_INSTCMD_HANDLED;
705 }
706
707
708 if (strcasecmp(cmdname, "shutdown.return") == 0)
709 {
710 OutBuff[0] = CMD_SD_ONESHOT ; /* turn ON the outputs */
711 OutBuff[1] = 0xFF ; /* ALL outputs */
712 if (ups.StatusUPS & 0x01)
713 OutBuff[2] = 0x02 ; /* Battery shutdown */
714 else
715 OutBuff[2] = 0x01 ; /* Online shutdown */
716
717 if (ups.ShutdownDelay < 6)
718 ups.ShutdownDelay = 6 ;
719
720 OutBuff[3] = (ups.ShutdownDelay >> 8) & 0xFF ; /* SDDELAY (MSB) Shutdown value (seconds) */
721 OutBuff[4] = (ups.ShutdownDelay & 0xFF) ; /* SDDELAY (LSB) */
722 OutBuff[5] = (ups.WakeUpDelay >> 16) & 0xFF ; /* WUDELAY (MSB) Wakeup value (seconds) */
723 OutBuff[6] = (ups.WakeUpDelay >> 8) & 0xFF ; /* WUDELAY (...) */
724 OutBuff[7] = (ups.WakeUpDelay & 0xFF ) ; /* WUDELAY (LSB) */
725
726 if ((p = CmdSerial(OutBuff, LEN_SD_ONESHOT, InpBuff)) != NULL)
727 {
728 p += 3 ; /* 'p' points to received data */
729 upslogx(LOG_INFO, "Shutdown command(TYPE=%02x, SD=%u, WU=%u)", OutBuff[2], ups.ShutdownDelay, ups.WakeUpDelay) ;
730 upsdebugx(3, "Shutdown command(TYPE=%02x, SD=%u, WU=%u): %s", OutBuff[2], ups.ShutdownDelay, ups.WakeUpDelay, PrintErr(ups.ErrCode));
731 }
732 else
733 {
734 upsdebugx(1, "Shutdown command(TYPE=%02x, SD=%u, WU=%u): %s", OutBuff[2], ups.ShutdownDelay, ups.WakeUpDelay, PrintErr(ups.ErrCode));
735 upslogx(LOG_ERR, "Shutdown command(SD=%u, WU=%u): %s", ups.ShutdownDelay, ups.WakeUpDelay, PrintErr(ups.ErrCode));
736 }
737 return STAT_INSTCMD_HANDLED;
738 }
739
740
741 if (strcasecmp(cmdname, "shutdown.stayoff") == 0)
742 {
743 OutBuff[0] = CMD_SD_ONESHOT ; /* turn ON the outputs */
744 OutBuff[1] = 0xFF ; /* ALL outputs */
745 if (ups.StatusUPS & 0x01)
746 OutBuff[2] = 0x02 ; /* Battery shutdown */
747 else
748 OutBuff[2] = 0x01 ; /* Online shutdown */
749
750 if (ups.ShutdownDelay < 6)
751 ups.ShutdownDelay = 6 ;
752
753 OutBuff[3] = (ups.ShutdownDelay >> 8) & 0xFF ; /* SDDELAY (MSB) Shutdown value (seconds) */
754 OutBuff[4] = (ups.ShutdownDelay & 0xFF) ; /* SDDELAY (LSB) */
755 OutBuff[5] = 0 ; /* WUDELAY (MSB) Wakeup value (seconds) */
756 OutBuff[6] = 0 ; /* WUDELAY (...) */
757 OutBuff[7] = 0 ; /* WUDELAY (LSB) */
758
759 if ((p = CmdSerial(OutBuff, LEN_SD_ONESHOT, InpBuff)) != NULL)
760 {
761 p += 3 ; /* 'p' points to received data */
762 upslogx(LOG_INFO, "shutdown.stayoff - (TYPE=%02x, SD=%u, WU=%u)", OutBuff[2], ups.ShutdownDelay, 0) ;
763 upsdebugx(3, "shutdown.stayoff - (TYPE=%02x, SD=%u, WU=%u): %s", OutBuff[2], ups.ShutdownDelay, 0, PrintErr(ups.ErrCode));
764 }
765 else
766 {
767 upsdebugx(1, "shutdown.stayoff - (TYPE=%02x, SD=%u, WU=%u): %s", OutBuff[2], ups.ShutdownDelay, 0, PrintErr(ups.ErrCode));
768 upslogx(LOG_ERR, "shutdown.stayoff - (TYPE=%02x, SD=%u, WU=%u)", OutBuff[2], ups.ShutdownDelay, 0) ;
769 }
770 return STAT_INSTCMD_HANDLED;
771 }
772
773 return STAT_INSTCMD_UNKNOWN;
774 }
775
776 #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_BESIDEFUNC) && (!defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_INSIDEFUNC) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS_BESIDEFUNC) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE_BESIDEFUNC) || defined (HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_COMPARE_BESIDEFUNC) )
777 # pragma GCC diagnostic push
778 #endif
779 #if (!defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_INSIDEFUNC) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS_BESIDEFUNC)
780 # pragma GCC diagnostic ignored "-Wtype-limits"
781 #endif
782 #if (!defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_INSIDEFUNC) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE_BESIDEFUNC)
783 # pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare"
784 #endif
785 #if (!defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_INSIDEFUNC) && (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_COMPARE_BESIDEFUNC)
786 # pragma GCC diagnostic ignored "-Wtautological-compare"
787 #endif
setvar(const char * varname,const char * val)788 int setvar(const char *varname, const char *val)
789 {
790 unsigned int delay;
791
792 if (sscanf(val, "%u", &delay) != 1)
793 {
794 return STAT_SET_UNKNOWN;
795 }
796
797 if (strcasecmp(varname, "ups.delay.start") == 0)
798 {
799 #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || defined (HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_COMPARE) )
800 # pragma GCC diagnostic push
801 #endif
802 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS
803 # pragma GCC diagnostic ignored "-Wtype-limits"
804 #endif
805 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE
806 # pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare"
807 #endif
808 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_COMPARE
809 # pragma GCC diagnostic ignored "-Wtautological-compare"
810 #endif
811 /* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */
812 #ifdef __clang__
813 #pragma clang diagnostic push
814 #pragma clang diagnostic ignored "-Wtautological-compare"
815 #pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare"
816 #endif
817 delay = CLAMP(delay, 0, MAX_START_DELAY);
818 #ifdef __clang__
819 #pragma clang diagnostic pop
820 #endif
821 #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || defined (HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_COMPARE) )
822 # pragma GCC diagnostic pop
823 #endif
824 upsdebugx(1, "set 'WUDELAY': %u/%u", delay, ups.WakeUpDelay);
825 ups.WakeUpDelay = delay ;
826 dstate_setinfo("ups.delay.start", "%u", ups.WakeUpDelay);
827 dstate_dataok();
828 return STAT_SET_HANDLED;
829 }
830
831 if (strcasecmp(varname, "ups.delay.shutdown") == 0)
832 {
833 #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || defined (HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_COMPARE) )
834 # pragma GCC diagnostic push
835 #endif
836 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS
837 # pragma GCC diagnostic ignored "-Wtype-limits"
838 #endif
839 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE
840 # pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare"
841 #endif
842 #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_COMPARE
843 # pragma GCC diagnostic ignored "-Wtautological-compare"
844 #endif
845 /* Older CLANG (e.g. clang-3.4) seems to not support the GCC pragmas above */
846 #ifdef __clang__
847 #pragma clang diagnostic push
848 #pragma clang diagnostic ignored "-Wtautological-compare"
849 #pragma clang diagnostic ignored "-Wtautological-constant-out-of-range-compare"
850 #endif
851 delay = CLAMP(delay, 0, MAX_SHUTDOWN_DELAY);
852 #ifdef __clang__
853 #pragma clang diagnostic pop
854 #endif
855 #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE) || defined (HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_COMPARE) )
856 # pragma GCC diagnostic pop
857 #endif
858 upsdebugx(1, "set 'SDDELAY': %u/%u", delay, ups.ShutdownDelay);
859 ups.ShutdownDelay = delay;
860 dstate_setinfo("ups.delay.shutdown", "%u", ups.ShutdownDelay);
861 dstate_dataok();
862 return STAT_SET_HANDLED;
863 }
864
865 return STAT_SET_UNKNOWN;
866 }
867 #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_BESIDEFUNC) && (!defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP_INSIDEFUNC) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS_BESIDEFUNC) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE_BESIDEFUNC) || defined (HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_COMPARE_BESIDEFUNC) )
868 # pragma GCC diagnostic pop
869 #endif
870
upsdrv_initinfo(void)871 void upsdrv_initinfo(void)
872 {
873 /* Get vars from ups.conf */
874 if (getval("ups.delay.shutdown")) {
875 int ipv = atoi(getval("ups.delay.shutdown"));
876 ups.ShutdownDelay = (unsigned int) CLAMP(ipv, 0, MAX_SHUTDOWN_DELAY);
877 }
878 else {
879 ups.ShutdownDelay = 120; /* Shutdown delay in seconds */
880 }
881
882 if (getval("ups.delay.start")) {
883 int ipv = atoi(getval("ups.delay.start"));
884 ups.WakeUpDelay = (unsigned int) CLAMP(ipv, 0, MAX_START_DELAY);
885 }
886 else {
887 ups.WakeUpDelay = 10; /* WakeUp delay in seconds */
888 }
889
890 if (detect_hardware() == -1)
891 {
892 fatalx(EXIT_FAILURE,
893 "Unable to detect a Microdowell's Enterprise UPS on port %s\nCheck the cable, port name and try again", device_path);
894 }
895
896 /* I set the correspondig UPS variables
897 They were read in 'detect_hardware()'
898 some other variables were set in 'detect_hardware()' */
899 dstate_setinfo("ups.model", "Enterprise N%s", ups.UpsModel+3) ;
900 dstate_setinfo("ups.power.nominal", "%d", atoi(ups.UpsModel+3) * 100) ;
901 dstate_setinfo("ups.realpower.nominal", "%d", atoi(ups.UpsModel+3) * 60) ;
902
903 ups.ge_2kVA = 0 ; /* differentiate between 2 type of UPSs */
904 if (atoi(ups.UpsModel+3) >= 20)
905 ups.ge_2kVA = 1 ;
906
907 dstate_setinfo("ups.type", "online-interactive") ;
908 dstate_setinfo("ups.serial", "%s", ups.SerialNumber) ;
909 dstate_setinfo("ups.firmware", "%d.%d (%d)", ups.FW_MajorVersion, ups.FW_MinorVersion, ups.FW_SubVersion) ;
910 dstate_setinfo("ups.firmware.aux", "%d.%d %d.%d", ups.HW_MajorVersion, ups.HW_MinorVersion,
911 ups.BR_MajorVersion, ups.BR_MinorVersion) ;
912 dstate_setinfo("ups.mfr", "Microdowell") ;
913 dstate_setinfo("ups.mfr.date", "%04d/%02d/%02d", ups.YearOfProd, ups.MonthOfProd, ups.DayOfProd) ;
914 dstate_setinfo("battery.packs", "%d", ups.BatteryNumber) ;
915
916 /* Register the available variables. */
917 dstate_setinfo("ups.delay.start", "%d", ups.WakeUpDelay);
918 dstate_setflags("ups.delay.start", ST_FLAG_RW | ST_FLAG_STRING);
919 dstate_setaux("ups.delay.start", MAX_START_DELAY_LEN);
920
921 dstate_setinfo("ups.delay.shutdown", "%d", ups.ShutdownDelay);
922 dstate_setflags("ups.delay.shutdown", ST_FLAG_RW | ST_FLAG_STRING);
923 dstate_setaux("ups.delay.shutdown", MAX_SHUTDOWN_DELAY_LEN);
924
925 dstate_addcmd("load.on");
926 dstate_addcmd("load.off");
927 dstate_addcmd("shutdown.return");
928 dstate_addcmd("shutdown.stayoff");
929
930 /* Register the available instant commands. */
931 /*
932 dstate_addcmd("test.battery.start");
933 dstate_addcmd("test.battery.stop");
934 dstate_addcmd("shutdown.stop");
935 dstate_addcmd("beeper.toggle");
936 */
937
938 /* set handlers */
939 upsh.instcmd = instcmd ;
940 upsh.setvar = setvar;
941 }
942
upsdrv_shutdown(void)943 void upsdrv_shutdown(void)
944 {
945 unsigned char OutBuff[20] ;
946 unsigned char InpBuff[260] ;
947 unsigned char *p ;
948 unsigned char BatteryFlag=0 ;
949
950 OutBuff[0] = CMD_GET_STATUS ; /* get UPS status */
951 if ((p = CmdSerial(OutBuff, LEN_GET_STATUS, InpBuff)) != NULL)
952 {
953 p += 3 ; /* 'p' points to received data */
954
955 status_init(); /* reset status flags */
956
957 /* store last UPS status */
958 ups.StatusUPS = (uint32_t)p[0] ;
959 ups.StatusUPS |= ((uint32_t)p[1]<<8) ;
960 ups.StatusUPS |= ((uint32_t)p[2]<<16) ;
961 ups.StatusUPS |= ((uint32_t)p[3]<<24) ;
962 ups.ShortStatus = (uint16_t)p[0] ;
963 ups.ShortStatus |= ((uint16_t)p[1]<<8) ;
964 upsdebugx(1, "ups.StatusUPS: %08" PRIX32, ups.StatusUPS);
965 upsdebugx(1, "ups.ShortStatus: %04" PRIX16, ups.ShortStatus);
966
967 /* on battery? */
968 if (p[0] & 0x01)
969 BatteryFlag = 1 ; /* YES */
970 upsdebugx(3, "get 'Get Status': %s", PrintErr(ups.ErrCode));
971 }
972 else
973 {
974 upsdebugx(1, "get 'Get Status': %s", PrintErr(ups.ErrCode));
975 /* upslogx(LOG_ERR, "get 'Get Status': %s", PrintErr(ups.ErrCode)); */
976 }
977
978
979 /* Send SHUTDOWN command */
980 OutBuff[0] = CMD_SD_ONESHOT ; /* Send SHUTDOWN command */
981 OutBuff[1] = 0xFF ; /* shutdown on ALL ports */
982
983 /* is the UPS on battery? */
984 if (BatteryFlag)
985 OutBuff[2] = 0x02 ; /* Type of shutdown (BATTERY MODE) */
986 else
987 OutBuff[2] = 0x01 ; /* Type of shutdown (ONLINE) */
988
989 if (ups.ShutdownDelay < 6)
990 ups.ShutdownDelay = 6 ;
991
992 OutBuff[3] = (ups.ShutdownDelay >> 8) & 0xFF ; /* SDDELAY (MSB) Shutdown value (seconds) */
993 OutBuff[4] = (ups.ShutdownDelay & 0xFF) ; /* SDDELAY (LSB) */
994 OutBuff[5] = (ups.WakeUpDelay >> 16) & 0xFF ; /* WUDELAY (MSB) Wakeup value (seconds) */
995 OutBuff[6] = (ups.WakeUpDelay >> 8) & 0xFF ; /* WUDELAY (...) */
996 OutBuff[7] = (ups.WakeUpDelay & 0xFF ) ; /* WUDELAY (LSB) */
997
998 if ((p = CmdSerial(OutBuff, LEN_SD_ONESHOT, InpBuff)) != NULL)
999 {
1000 upsdebugx(2, "Shutdown command(TYPE=%02x, SD=%u, WU=%u): %s", OutBuff[2], ups.ShutdownDelay, ups.WakeUpDelay, PrintErr(ups.ErrCode));
1001 }
1002 else
1003 {
1004 /* command not sent: print error code */
1005 upsdebugx(1, "Shutdown command(TYPE=%02x, SD=%u, WU=%u): %s", OutBuff[2], ups.ShutdownDelay, ups.WakeUpDelay, PrintErr(ups.ErrCode));
1006 upslogx(LOG_ERR, "Shutdown command(SD=%u, WU=%u): %s", ups.ShutdownDelay, ups.WakeUpDelay, PrintErr(ups.ErrCode));
1007 }
1008 }
1009
1010
upsdrv_help(void)1011 void upsdrv_help(void)
1012 {
1013 }
1014
1015 /* list flags and values that you want to receive via -x */
upsdrv_makevartable(void)1016 void upsdrv_makevartable(void)
1017 {
1018 /* allow '-x xyzzy' */
1019 /* addvar(VAR_FLAG, "xyzzy", "Enable xyzzy mode"); */
1020
1021 /* allow '-x foo=<some value>' */
1022 addvar(VAR_VALUE, "ups.delay.shutdown", "Override shutdown delay (120s)");
1023 addvar(VAR_VALUE, "ups.delay.start", "Override restart delay (10s)");
1024 }
1025
upsdrv_initups(void)1026 void upsdrv_initups(void)
1027 {
1028 upsfd = ser_open(device_path) ;
1029
1030 ser_set_speed(upsfd, device_path, B19200) ;
1031
1032 /* need to clear RTS and DTR: otherwise with default cable, communication will be problematic
1033 It is the same as removing pin7 from cable (pin 7 is needed for Plug&Play compatibility) */
1034 ser_set_dtr(upsfd, 0);
1035 ser_set_rts(upsfd, 0);
1036
1037 usleep(10000) ; /* small delay (1/100 s)) */
1038 }
1039
upsdrv_cleanup(void)1040 void upsdrv_cleanup(void)
1041 {
1042 /* free(dynamic_mem); */
1043 ser_close(upsfd, device_path) ;
1044 }
1045