1 /*
2  bcmxcp.c - driver for powerware UPS
3 
4  Total rewrite of bcmxcp.c (nut ver-1.4.3)
5  * Copyright (c) 2002, Martin Schroeder *
6  * emes -at- geomer.de *
7  * All rights reserved.*
8 
9  Copyright (C)
10    2004 Kjell Claesson <kjell.claesson-at-epost.tidanet.se>
11    2004 Tore Ørpetveit <tore-at-orpetveit.net>
12    2011 - 2015 Arnaud Quette <ArnaudQuette@Eaton.com>
13 
14  Thanks to Tore Ørpetveit <tore-at-orpetveit.net> that sent me the
15  manuals for bcm/xcp.
16 
17  And to Fabio Di Niro <fabio.diniro@email.it> and his metasys module.
18  It influenced the layout of this driver.
19 
20  Modified for USB by Wolfgang Ocker <weo@weo1.de>
21 
22  ojw0000 2007Apr5 Oliver Wilcock - modified to control individual load segments (outlet.2.shutdown.return) on Powerware PW5125.
23 
24  Modified to support setvar for outlet.n.delay.start by Rich Wrenn (RFW) 9-3-11.
25  Modified to support setvar for outlet.n.delay.shutdown by Arnaud Quette, 9-12-11
26 
27 This program is free software; you can redistribute it and/or modify
28  it under the terms of the GNU General Public License as published by
29  the Free Software Foundation; either version 2 of the License, or
30  (at your option) any later version.
31 
32  This program is distributed in the hope that it will be useful,
33  but WITHOUT ANY WARRANTY; without even the implied warranty of
34  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
35  GNU General Public License for more details.
36 
37  You should have received a copy of the GNU General Public License
38  along with this program; if not, write to the Free Software
39  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
40 
41 TODO List:
42 
43         Extend the parsing of the Standard ID Block, to read:
44 
45                 Config Block Length: (High priority)
46                 Give information if config block is
47                 present, and how long it is, if it exist.
48                 If config block exist, read the config block and parse the
49                 'Length of the Extended Limits Configuration Block' for
50                 extended configuration commands
51 
52                 Statistic map Size: (Low priority)
53                 May be used to se if there is a Statistic Map.
54                 It holds data on the utility power quality for
55                 the past month and since last reset. Number of
56                 times on battery and how long. Up time and utility
57                 frequency deviation. (Only larger ups'es)
58 
59                 Size of Alarm History Log: (Low priority)
60                 See if it have any alarm history block and enable
61                 command to dump it.
62 
63                 Maximum Supported Command Length: ( Med. to High priority)
64                 Give info about the ups receive buffer size.
65 
66                 Size of Alarm Block: ( Med. to High priority)
67                 Make a smarter handling of the Active alarm's if we know the length
68                 of the Active Alarm Block. Don't need the long loop to parse the
69                 alarm's. Maybe use another way to set up the alarm struct in the
70                 'init_alarm_map'.
71 
72         Parse 'Communication Capabilities Block' ( Low priority)
73                 Get info of the connected ports ID, number of baud rates,
74                 command and respnse length.
75 
76         Parse 'Communication Port List Block': ( Low priority)
77                 This block gives info about the communication ports. Some ups'es
78                 have multiple comport's, and use one port for eatch load segment.
79                 In this block it is possible to get:
80                 Number of ports. (In this List)
81                 This Comport id (Which Comm Port is reporting this block.)
82                 Comport id (Id for eatch port listed. The first comport ID=1)
83                 Baudrate of the listed port.
84                 Serial config.
85                 Port usage:
86                         What this Comm Port is being used for:
87                         0 = Unknown usage, No communication occurring.
88                         1 = Undefined / Unknown communication occurring
89                         2 = Waiting to communicate with a UPS
90                         3 = Communication established with a UPS
91                         4 = Waiting to communicate with software or adapter
92                         5 = Communication established software (e.g., LanSafe)
93                                 or adapter (e.g., ConnectUPS)
94                         6 = Communicating with a Display Device
95                         7 = Multi-drop Serial channel
96                         8 = Communicating with an Outlet Controller
97                 Number of outlets. (Number of Outlets "assigned to" (controlled by) this Comm Port)
98                 Outlet number. (Each assigned Outlet is listed (1-64))
99 
100                 'Set outlet parameter command (0x97)' to alter the delay
101                 settings or turn the outlet on or off with a delay (0 - 32767 seconds)
102 
103 
104         Rewrite some parts of the driver, to minimise code duplication. (Like the instant commands)
105 
106         Implement support for Password Authorization (XCP spec, §4.3.2)
107 
108         Complete support for settable variables (upsh.setvar)
109 */
110 
111 
112 #include "main.h"
113 #include <math.h>       /* For ldexp() */
114 #include <float.h>      /*for FLT_MAX */
115 #include "nut_stdint.h" /* for uint8_t, uint16_t, uint32_t, ... */
116 #include "bcmxcp_io.h"
117 #include "bcmxcp.h"
118 
119 #define DRIVER_NAME    "BCMXCP UPS driver"
120 #define DRIVER_VERSION "0.31"
121 
122 #define MAX_NUT_NAME_LENGTH 128
123 #define NUT_OUTLET_POSITION   7
124 
125 /* driver description structure */
126 upsdrv_info_t upsdrv_info = {
127         DRIVER_NAME,
128         DRIVER_VERSION,
129         "Martin Schroeder <emes@geomer.de>\n" \
130         "Kjell Claesson <kjell.claesson@epost.tidanet.se>\n" \
131         "Tore Ørpetveit <tore@orpetveit.net>\n" \
132         "Arnaud Quette <ArnaudQuette@Eaton.com>\n" \
133         "Wolfgang Ocker <weo@weo1.de>\n" \
134         "Oliver Wilcock\n" \
135         "Prachi Gandhi <prachisgandhi@eaton.com>\n" \
136         "Alf Høgemark <alf@i100>\n" \
137         "Gavrilov Igor",
138         DRV_STABLE,
139         { &comm_upsdrv_info, NULL }
140 };
141 
142 static int get_word(const unsigned char*);
143 static long int get_long(const unsigned char*);
144 static float get_float(const unsigned char *data);
145 static void init_command_map(void);
146 static void init_meter_map(void);
147 static void init_alarm_map(void);
148 static bool_t init_command(int size);
149 static void init_config(void);
150 static void init_limit(void);
151 static void init_ext_vars(void);
152 static void init_topology(void);
153 static void init_ups_meter_map(const unsigned char *map, unsigned char len);
154 static void init_ups_alarm_map(const unsigned char *map, unsigned char len);
155 static bool_t set_alarm_support_in_alarm_map(const unsigned char *map, const int mapIndex, const int bitmask, const int alarmMapIndex, const int alarmBlockIndex);
156 static void decode_meter_map_entry(const unsigned char *entry, const unsigned char format, char* value);
157 static int init_outlet(unsigned char len);
158 static void init_system_test_capabilities(void);
159 static int instcmd(const char *cmdname, const char *extra);
160 static int setvar(const char *varname, const char *val);
161 static int decode_instcmd_exec(const int res, const unsigned char exec_status, const char *cmdname, const char *success_msg);
162 static int decode_setvar_exec(const int res, const unsigned char exec_status, const char *cmdname, const char *success_msg);
163 static float calculate_ups_load(const unsigned char *data);
164 
165 static const char *nut_find_infoval(info_lkp_t *xcp2info, const double value, const bool_t debug_output_nonexisting);
166 
167 const char *FreqTol[3] = {"+/-2%", "+/-5%", "+/-7"};
168 const char *ABMStatus[4] = {"charging", "discharging", "floating", "resting"};
169 const char *OutletStatus[9] = {"unknown","on/closed","off/open","on with pending","off with pending","unknown","unknown","failed and closed","failed and open"};
170 /* Standard Authorization Block */
171 unsigned char AUTHOR[4] = {0xCF, 0x69, 0xE8, 0xD5};
172 int nphases = 0;
173 int outlet_block_len = 0;
174 const char *cpu_name[5] = {"Cont:", "Inve:", "Rect:", "Netw:", "Disp:"};
175 const char *horn_stat[3] = {"disabled", "enabled", "muted"};
176 
177 
178 /* Battery test results */
179 info_lkp_t batt_test_info[] = {
180 	{ 0, "No test initiated", NULL },
181 	{ 1, "In progress", NULL },
182 	{ 2, "Done and passed", NULL },
183 	{ 3, "Aborted", NULL },
184 	{ 4, "Done and error", NULL },
185 	{ 5, "Test scheduled", NULL },
186 	/* Not sure about the meaning of the below ones! */
187 	{ 6, NULL, NULL }, /* The string was present but it has now been removed */
188 	{ 7, NULL, NULL }, /* The string was not installed at the last power up */
189 	{ 0, NULL, NULL }
190 };
191 
192 /* Topology map results */
193 info_lkp_t topology_info[] = {
194 	{ BCMXCP_TOPOLOGY_OFFLINE_SWITCHER_1P, "Off-line switcher, Single Phase", NULL },
195 	{ BCMXCP_TOPOLOGY_LINEINT_UPS_1P, "Line-Interactive UPS, Single Phase", NULL },
196 	{ BCMXCP_TOPOLOGY_LINEINT_UPS_2P, "Line-Interactive UPS, Two Phase", NULL },
197 	{ BCMXCP_TOPOLOGY_LINEINT_UPS_3P, "Line-Interactive UPS, Three Phase", NULL },
198 	{ BCMXCP_TOPOLOGY_DUAL_AC_ONLINE_UPS_1P, "Dual AC Input, On-Line UPS, Single Phase", NULL },
199 	{ BCMXCP_TOPOLOGY_DUAL_AC_ONLINE_UPS_2P, "Dual AC Input, On-Line UPS, Two Phase", NULL },
200 	{ BCMXCP_TOPOLOGY_DUAL_AC_ONLINE_UPS_3P, "Dual AC Input, On-Line UPS, Three Phase", NULL },
201 	{ BCMXCP_TOPOLOGY_ONLINE_UPS_1P, "On-Line UPS, Single Phase", NULL },
202 	{ BCMXCP_TOPOLOGY_ONLINE_UPS_2P, "On-Line UPS, Two Phase", NULL },
203 	{ BCMXCP_TOPOLOGY_ONLINE_UPS_3P, "On-Line UPS, Three Phase", NULL },
204 	{ BCMXCP_TOPOLOGY_PARA_REDUND_ONLINE_UPS_1P, "Parallel Redundant On-Line UPS, Single Phase", NULL },
205 	{ BCMXCP_TOPOLOGY_PARA_REDUND_ONLINE_UPS_2P, "Parallel Redundant On-Line UPS, Two Phase", NULL },
206 	{ BCMXCP_TOPOLOGY_PARA_REDUND_ONLINE_UPS_3P, "Parallel Redundant On-Line UPS, Three Phase", NULL },
207 	{ BCMXCP_TOPOLOGY_PARA_CAPACITY_ONLINE_UPS_1P, "Parallel for Capacity On-Line UPS, Single Phase", NULL },
208 	{ BCMXCP_TOPOLOGY_PARA_CAPACITY_ONLINE_UPS_2P, "Parallel for Capacity On-Line UPS, Two Phase", NULL },
209 	{ BCMXCP_TOPOLOGY_PARA_CAPACITY_ONLINE_UPS_3P, "Parallel for Capacity On-Line UPS, Three Phase", NULL },
210 	{ BCMXCP_TOPOLOGY_SYSTEM_BYPASS_MODULE_3P, "System Bypass Module, Three Phase", NULL },
211 	{ BCMXCP_TOPOLOGY_HOT_TIE_CABINET_3P, "Hot-Tie Cabinet, Three Phase", NULL },
212 	{ BCMXCP_TOPOLOGY_OUTLET_CONTROLLER_1P, "Outlet Controller, Single Phase", NULL },
213 	{ BCMXCP_TOPOLOGY_DUAL_AC_STATIC_SWITCH_3P, "Dual AC Input Static Switch Module, 3 Phase", NULL },
214 	{ 0, NULL, NULL }
215 };
216 
217 /* Command map results */
218 info_lkp_t command_map_info[] = {
219 	{ PW_INIT_BAT_TEST, "test.battery.start", NULL },
220 	{ PW_LOAD_OFF_RESTART, "shutdown.return", NULL },
221 	{ PW_UPS_OFF, "shutdown.stayoff", NULL },
222 	{ PW_UPS_ON, "load.on", NULL},
223 	{ PW_GO_TO_BYPASS, "bypass.start", NULL},
224 	{ 0, NULL, NULL }
225 };
226 
227 /* System test capabilities results */
228 info_lkp_t system_test_info[] = {
229 	{ PW_SYS_TEST_GENERAL, "test.system.start", NULL },
230 /*	{ PW_SYS_TEST_SCHEDULE_BATTERY_COMMISSION, "test.battery.start.delayed", NULL }, */
231 /*	{ PW_SYS_TEST_ALTERNATE_AC_INPUT, "test.alternate_acinput.start", NULL }, */
232 	{ PW_SYS_TEST_FLASH_LIGHTS, "test.panel.start", NULL },
233 	{ 0, NULL, NULL }
234 };
235 
236 /* allocate storage for shared variables (extern in bcmxcp.h) */
237 BCMXCP_COMMAND_MAP_ENTRY_t
238 	bcmxcp_command_map[BCMXCP_COMMAND_MAP_MAX];
239 BCMXCP_METER_MAP_ENTRY_t
240 	bcmxcp_meter_map[BCMXCP_METER_MAP_MAX];
241 BCMXCP_ALARM_MAP_ENTRY_t
242 	bcmxcp_alarm_map[BCMXCP_ALARM_MAP_MAX];
243 BCMXCP_STATUS_t
244 	bcmxcp_status;
245 
246 
247 /* get_word function from nut driver metasys.c */
get_word(const unsigned char * buffer)248 int get_word(const unsigned char *buffer) /* return an integer reading a word in the supplied buffer */
249 {
250 	unsigned char a, b;
251 	int result;
252 
253 	a = buffer[0];
254 	b = buffer[1];
255 	result = b*256 + a;
256 
257 	return result;
258 }
259 
260 /* get_long function from nut driver metasys.c for meter readings*/
get_long(const unsigned char * buffer)261 long int get_long(const unsigned char *buffer) /* return a long integer reading 4 bytes in the supplied buffer.*/
262 {
263 	unsigned char a, b, c, d;
264 	long int result;
265 
266 	a = buffer[0];
267 	b = buffer[1];
268 	c = buffer[2];
269 	d = buffer[3];
270 	result = (256*256*256*d) + (256*256*c) + (256*b) + a;
271 
272 	return result;
273 }
274 
275 /* get_float funktion for convering IEEE-754 to float */
get_float(const unsigned char * data)276 float get_float(const unsigned char *data)
277 {
278 	int s, e;
279 	unsigned long src;
280 	long f;
281 
282 	src = ((unsigned long)data[3] << 24) |
283 	((unsigned long)data[2] << 16) |
284 	((unsigned long)data[1] << 8) |
285 	((unsigned long)data[0]);
286 
287 	s = (src & 0x80000000UL) >> 31;
288 	e = (src & 0x7F800000UL) >> 23;
289 	f = (src & 0x007FFFFFUL);
290 
291 	if (e == 255 && f != 0)
292 	{
293 		/* NaN (Not a Number) */
294 		return FLT_MAX;
295 	}
296 
297 	if (e == 255 && f == 0 && s == 1)
298 	{
299 		/* Negative infinity */
300 		return -FLT_MAX;
301 	}
302 
303 	if (e == 255 && f == 0 && s == 0)
304 	{
305 		/* Positive infinity */
306 		return FLT_MAX;
307 	}
308 
309 	if (e > 0 && e < 255)
310 	{
311 		/* Normal number */
312 		f += 0x00800000UL;
313 		if (s) f = -f;
314 		return ldexp(f, e - 150);
315 	}
316 
317 	if (e == 0 && f != 0)
318 	{
319 		/* Denormal number */
320 		if (s) f = -f;
321 		return ldexp(f, -149);
322 	}
323 
324 	if (e == 0 && f == 0 && (s == 1 || s == 0))
325 	{
326 		/* Zero */
327 		return 0;
328 	}
329 
330 	/* Never happens */
331 	upslogx(LOG_ERR, "s = %d, e = %d, f = %lu\n", s, e, f);
332 	return 0;
333 }
334 
335 /* lightweight function to calculate the 8-bit
336  * two's complement checksum of buf, using XCP data length (including header)
337  * the result must be 0 for the sequence data to be valid */
checksum_test(const unsigned char * buf)338 int checksum_test(const unsigned char *buf)
339 {
340 	unsigned char checksum = 0;
341 	int i, length;
342 
343 	/* buf[2] is the length of the XCP frame ; add 5 for the header */
344 	length = (int)(buf[2]) + 5;
345 
346 	for (i = 0; i < length; i++) {
347 		checksum += buf[i];
348 	}
349 	/* Compute the 8-bit, Two's Complement checksum now and return it */
350 	checksum = ((0x100 - checksum) & 0xFF);
351 	return (checksum == 0);
352 }
353 
calc_checksum(const unsigned char * buf)354 unsigned char calc_checksum(const unsigned char *buf)
355 {
356 	unsigned char c;
357 	int i;
358 
359 	c = 0;
360 	for(i = 0; i < 2 + buf[1]; i++)
361 		c -= buf[i];
362 
363 	return c;
364 }
365 
init_command_map()366 void init_command_map()
367 {
368 	int i = 0;
369 
370 	/* Clean entire map */
371 	memset(&bcmxcp_command_map, 0, sizeof(BCMXCP_COMMAND_MAP_ENTRY_t) * BCMXCP_COMMAND_MAP_MAX);
372 
373 	/* Set all command descriptions */
374 	bcmxcp_command_map[PW_ID_BLOCK_REQ].command_desc = "PW_ID_BLOCK_REQ";
375 	bcmxcp_command_map[PW_EVENT_HISTORY_LOG_REQ].command_desc = "PW_EVENT_HISTORY_LOG_REQ";
376 	bcmxcp_command_map[PW_STATUS_REQ].command_desc = "PW_STATUS_REQ";
377 	bcmxcp_command_map[PW_METER_BLOCK_REQ].command_desc = "PW_METER_BLOCK_REQ";
378 	bcmxcp_command_map[PW_CUR_ALARM_REQ].command_desc = "PW_CUR_ALARM_REQ";
379 	bcmxcp_command_map[PW_CONFIG_BLOCK_REQ].command_desc = "PW_CONFIG_BLOCK_REQ";
380 	bcmxcp_command_map[PW_UTILITY_STATISTICS_BLOCK_REQ].command_desc = "PW_UTILITY_STATISTICS_BLOCK_REQ";
381 	bcmxcp_command_map[PW_WAVEFORM_BLOCK_REQ].command_desc = "PW_WAVEFORM_BLOCK_REQ";
382 	bcmxcp_command_map[PW_BATTERY_REQ].command_desc = "PW_BATTERY_REQ";
383 	bcmxcp_command_map[PW_LIMIT_BLOCK_REQ].command_desc = "PW_LIMIT_BLOCK_REQ";
384 	bcmxcp_command_map[PW_TEST_RESULT_REQ].command_desc = "PW_TEST_RESULT_REQ";
385 	bcmxcp_command_map[PW_COMMAND_LIST_REQ].command_desc = "PW_COMMAND_LIST_REQ";
386 	bcmxcp_command_map[PW_OUT_MON_BLOCK_REQ].command_desc = "PW_OUT_MON_BLOCK_REQ";
387 	bcmxcp_command_map[PW_COM_CAP_REQ].command_desc = "PW_COM_CAP_REQ";
388 	bcmxcp_command_map[PW_UPS_TOP_DATA_REQ].command_desc = "PW_UPS_TOP_DATA_REQ";
389 	bcmxcp_command_map[PW_COM_PORT_LIST_BLOCK_REQ].command_desc = "PW_COM_PORT_LIST_BLOCK_REQ";
390 	bcmxcp_command_map[PW_REQUEST_SCRATCHPAD_DATA_REQ].command_desc = "PW_REQUEST_SCRATCHPAD_DATA_REQ";
391 	bcmxcp_command_map[PW_GO_TO_BYPASS].command_desc = "PW_GO_TO_BYPASS";
392 	bcmxcp_command_map[PW_UPS_ON].command_desc = "PW_UPS_ON";
393 	bcmxcp_command_map[PW_LOAD_OFF_RESTART].command_desc = "PW_LOAD_OFF_RESTART";
394 	bcmxcp_command_map[PW_UPS_OFF].command_desc = "PW_UPS_OFF";
395 	bcmxcp_command_map[PW_DECREMENT_OUTPUT_VOLTAGE].command_desc = "PW_DECREMENT_OUTPUT_VOLTAGE";
396 	bcmxcp_command_map[PW_INCREMENT_OUTPUT_VOLTAGE].command_desc = "PW_INCREMENT_OUTPUT_VOLTAGE";
397 	bcmxcp_command_map[PW_SET_TIME_AND_DATE].command_desc = "PW_SET_TIME_AND_DATE";
398 	bcmxcp_command_map[PW_UPS_ON_TIME].command_desc = "PW_UPS_ON_TIME";
399 	bcmxcp_command_map[PW_UPS_ON_AT_TIME].command_desc = "PW_UPS_ON_AT_TIME";
400 	bcmxcp_command_map[PW_UPS_OFF_TIME].command_desc = "PW_UPS_OFF_TIME";
401 	bcmxcp_command_map[PW_UPS_OFF_AT_TIME].command_desc = "PW_UPS_OFF_AT_TIME";
402 	bcmxcp_command_map[PW_SET_CONF_COMMAND].command_desc = "PW_SET_CONF_COMMAND";
403 	bcmxcp_command_map[PW_SET_OUTLET_COMMAND].command_desc = "PW_SET_OUTLET_COMMAND";
404 	bcmxcp_command_map[PW_SET_COM_COMMAND].command_desc = "PW_SET_COM_COMMAND";
405 	bcmxcp_command_map[PW_SET_SCRATHPAD_SECTOR].command_desc = "PW_SET_SCRATHPAD_SECTOR";
406 	bcmxcp_command_map[PW_SET_POWER_STRATEGY].command_desc = "PW_SET_POWER_STRATEGY";
407 	bcmxcp_command_map[PW_SET_REQ_ONLY_MODE].command_desc = "PW_SET_REQ_ONLY_MODE";
408 	bcmxcp_command_map[PW_SET_UNREQUESTED_MODE].command_desc = "PW_SET_UNREQUESTED_MODE";
409 	bcmxcp_command_map[PW_INIT_BAT_TEST].command_desc = "PW_INIT_BAT_TEST";
410 	bcmxcp_command_map[PW_INIT_SYS_TEST].command_desc = "PW_INIT_SYS_TEST";
411 	bcmxcp_command_map[PW_SELECT_SUBMODULE].command_desc = "PW_SELECT_SUBMODULE";
412 	bcmxcp_command_map[PW_AUTHORIZATION_CODE].command_desc = "PW_AUTHORIZATION_CODE";
413 
414 	for(i = 0; i < BCMXCP_COMMAND_MAP_MAX; i++) {
415 		bcmxcp_command_map[i].command_byte = 0;
416 	}
417 }
418 
init_meter_map()419 void init_meter_map()
420 {
421 	/* Clean entire map */
422 	memset(&bcmxcp_meter_map, 0, sizeof(BCMXCP_METER_MAP_ENTRY_t) * BCMXCP_METER_MAP_MAX);
423 
424 	/* Set all corresponding mappings NUT <-> BCM/XCP */
425 	bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VOLTS_AB].nut_entity = "output.L1-L2.voltage";
426 	bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VOLTS_BC].nut_entity = "output.L2-L3.voltage";
427 	bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VOLTS_CA].nut_entity = "output.L3-L1.voltage";
428 	bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_VOLTS_AB].nut_entity = "input.L1-L2.voltage";
429 	bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_VOLTS_BC].nut_entity = "input.L2-L3.voltage";
430 	bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_VOLTS_CA].nut_entity = "input.L3-L1.voltage";
431 	bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_CURRENT_PHASE_B].nut_entity = "input.L2.current";
432 	bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_CURRENT_PHASE_C].nut_entity = "input.L3.current";
433 	bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_WATTS].nut_entity = "input.realpower";
434 	bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA].nut_entity = "ups.power";
435 	bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_VA].nut_entity = "input.power";
436 	bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_POWER_FACTOR].nut_entity = "output.powerfactor";
437 	bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_POWER_FACTOR].nut_entity = "input.powerfactor";
438 
439 	if (nphases == 1) {
440 		bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_CURRENT_PHASE_A].nut_entity = "input.current";
441 		bcmxcp_meter_map[BCMXCP_METER_MAP_PERCENT_LOAD_PHASE_A].nut_entity = "ups.load"; /* TODO: Decide on corresponding three-phase variable mapping. */
442 		bcmxcp_meter_map[BCMXCP_METER_MAP_BYPASS_VOLTS_PHASE_A].nut_entity = "input.bypass.voltage";
443 		bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_VOLTS_PHASE_A].nut_entity = "input.voltage";
444 		bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A].nut_entity = "output.current";
445 		bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A_BAR_CHART].nut_entity = "output.current.nominal";
446 		bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VOLTS_A].nut_entity = "output.voltage";
447 		bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_WATTS].nut_entity = "ups.realpower";
448 	} else {
449 		bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_CURRENT_PHASE_A].nut_entity = "input.L1.current";
450 		bcmxcp_meter_map[BCMXCP_METER_MAP_PERCENT_LOAD_PHASE_A].nut_entity = "output.L1.power.percent";
451 		bcmxcp_meter_map[BCMXCP_METER_MAP_PERCENT_LOAD_PHASE_B].nut_entity = "output.L2.power.percent";
452 		bcmxcp_meter_map[BCMXCP_METER_MAP_PERCENT_LOAD_PHASE_C].nut_entity = "output.L3.power.percent";
453 		bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA_PHASE_A].nut_entity = "output.L1.power";
454 		bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA_PHASE_B].nut_entity = "output.L2.power";
455 		bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA_PHASE_C].nut_entity = "output.L3.power";
456 		bcmxcp_meter_map[BCMXCP_METER_MAP_BYPASS_VOLTS_PHASE_A].nut_entity = "input.bypass.L1-N.voltage";
457 		bcmxcp_meter_map[BCMXCP_METER_MAP_BYPASS_VOLTS_PHASE_B].nut_entity = "input.bypass.L2-N.voltage";
458 		bcmxcp_meter_map[BCMXCP_METER_MAP_BYPASS_VOLTS_PHASE_C].nut_entity = "input.bypass.L3-N.voltage";
459 		bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_VOLTS_PHASE_A].nut_entity = "input.L1-N.voltage";
460 		bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A].nut_entity = "output.L1.current";
461 		bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A_BAR_CHART].nut_entity = "output.L1.current.nominal";
462 		bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VOLTS_A].nut_entity = "output.L1-N.voltage";
463 	}
464 	bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_FREQUENCY].nut_entity = "output.frequency";
465 	bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_FREQUENCY].nut_entity = "input.frequency";
466 	bcmxcp_meter_map[BCMXCP_METER_MAP_BYPASS_FREQUENCY].nut_entity = "input.bypass.frequency";
467 	bcmxcp_meter_map[BCMXCP_METER_MAP_BATTERY_CURRENT].nut_entity = "battery.current";
468 	bcmxcp_meter_map[BCMXCP_METER_MAP_BATTERY_VOLTAGE].nut_entity = "battery.voltage";
469 	bcmxcp_meter_map[BCMXCP_METER_MAP_PERCENT_BATTERY_LEFT].nut_entity = "battery.charge";
470 	bcmxcp_meter_map[BCMXCP_METER_MAP_BATTERY_TIME_REMAINING].nut_entity = "battery.runtime";
471 	bcmxcp_meter_map[BCMXCP_METER_MAP_BATTERY_DCUV_BAR_CHART].nut_entity = "battery.voltage.low";
472 	bcmxcp_meter_map[BCMXCP_METER_MAP_LOW_BATTERY_WARNING_V_BAR_CHART].nut_entity = "battery.charge.low";
473 	bcmxcp_meter_map[BCMXCP_METER_MAP_BATTERY_DISCHARGING_CURRENT_BAR_CHART].nut_entity = "battery.current.total";
474 	bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_VOLTS_PHASE_B].nut_entity = "input.L2-N.voltage";
475 	bcmxcp_meter_map[BCMXCP_METER_MAP_INPUT_VOLTS_PHASE_C].nut_entity = "input.L3-N.voltage";
476 	bcmxcp_meter_map[BCMXCP_METER_MAP_AMBIENT_TEMPERATURE].nut_entity = "ambient.temperature";
477 	bcmxcp_meter_map[BCMXCP_METER_MAP_HEATSINK_TEMPERATURE].nut_entity = "ups.temperature";
478 	bcmxcp_meter_map[BCMXCP_METER_MAP_POWER_SUPPLY_TEMPERATURE].nut_entity = "ambient.1.temperature";
479 	bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_B].nut_entity = "output.L2.current";
480 	bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_C].nut_entity = "output.L3.current";
481 	bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_B_BAR_CHART].nut_entity = "output.L2.current.nominal";
482 	bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_C_BAR_CHART].nut_entity = "output.L3.current.nominal";
483 	bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA_BAR_CHART].nut_entity = "ups.power.nominal";
484 	bcmxcp_meter_map[BCMXCP_METER_MAP_DATE].nut_entity = "ups.date";
485 	bcmxcp_meter_map[BCMXCP_METER_MAP_TIME].nut_entity = "ups.time";
486 	bcmxcp_meter_map[BCMXCP_METER_MAP_BATTERY_TEMPERATURE].nut_entity = "battery.temperature";
487 	bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VOLTS_B].nut_entity = "output.L2-N.voltage";
488 	bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VOLTS_C].nut_entity = "output.L3-N.voltage";
489 	bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_WATTS_PHASE_A].nut_entity = "ups.L1-N.realpower";
490 	bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_WATTS_PHASE_B].nut_entity = "ups.L2-N.realpower";
491 	bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_WATTS_PHASE_C].nut_entity = "ups.L3-N.realpower";
492 	bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_WATTS_PHASE_A_B_C_BAR_CHART].nut_entity = "ups.realpower.nominal";
493 	bcmxcp_meter_map[BCMXCP_METER_MAP_LINE_EVENT_COUNTER].nut_entity = "input.quality";
494 }
495 
init_alarm_map()496 void init_alarm_map()
497 {
498 	/* Clean entire map */
499 	memset(&bcmxcp_alarm_map, 0, sizeof(BCMXCP_ALARM_MAP_ENTRY_t) * BCMXCP_ALARM_MAP_MAX);
500 
501 	/* Set all alarm descriptions */
502 	bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_AC_OVER_VOLTAGE].alarm_desc = "INVERTER_AC_OVER_VOLTAGE";
503 	bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_AC_UNDER_VOLTAGE].alarm_desc = "INVERTER_AC_UNDER_VOLTAGE";
504 	bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_OVER_OR_UNDER_FREQ].alarm_desc = "INVERTER_OVER_OR_UNDER_FREQ";
505 	bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_AC_OVER_VOLTAGE].alarm_desc = "BYPASS_AC_OVER_VOLTAGE";
506 	bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_AC_UNDER_VOLTAGE].alarm_desc = "BYPASS_AC_UNDER_VOLTAGE";
507 	bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_OVER_OR_UNDER_FREQ].alarm_desc = "BYPASS_OVER_OR_UNDER_FREQ";
508 	bcmxcp_alarm_map[BCMXCP_ALARM_INPUT_AC_OVER_VOLTAGE].alarm_desc = "INPUT_AC_OVER_VOLTAGE";
509 	bcmxcp_alarm_map[BCMXCP_ALARM_INPUT_AC_UNDER_VOLTAGE].alarm_desc = "INPUT_AC_UNDER_VOLTAGE";
510 	bcmxcp_alarm_map[BCMXCP_ALARM_INPUT_UNDER_OR_OVER_FREQ].alarm_desc = "INPUT_UNDER_OR_OVER_FREQ";
511 	bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_OVER_VOLTAGE].alarm_desc = "OUTPUT_OVER_VOLTAGE";
512 	bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_UNDER_VOLTAGE].alarm_desc = "OUTPUT_UNDER_VOLTAGE";
513 	bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_UNDER_OR_OVER_FREQ].alarm_desc = "OUTPUT_UNDER_OR_OVER_FREQ";
514 	bcmxcp_alarm_map[BCMXCP_ALARM_REMOTE_EMERGENCY_PWR_OFF].alarm_desc = "REMOTE_EMERGENCY_PWR_OFF";
515 	bcmxcp_alarm_map[BCMXCP_ALARM_REMOTE_GO_TO_BYPASS].alarm_desc = "REMOTE_GO_TO_BYPASS";
516 	bcmxcp_alarm_map[BCMXCP_ALARM_BUILDING_ALARM_6].alarm_desc = "BUILDING_ALARM_6";
517 	bcmxcp_alarm_map[BCMXCP_ALARM_BUILDING_ALARM_5].alarm_desc = "BUILDING_ALARM_5";
518 	bcmxcp_alarm_map[BCMXCP_ALARM_BUILDING_ALARM_4].alarm_desc = "BUILDING_ALARM_4";
519 	bcmxcp_alarm_map[BCMXCP_ALARM_BUILDING_ALARM_3].alarm_desc = "BUILDING_ALARM_3";
520 	bcmxcp_alarm_map[BCMXCP_ALARM_BUILDING_ALARM_2].alarm_desc = "BUILDING_ALARM_2";
521 	bcmxcp_alarm_map[BCMXCP_ALARM_BUILDING_ALARM_1].alarm_desc = "BUILDING_ALARM_1";
522 	bcmxcp_alarm_map[BCMXCP_ALARM_STATIC_SWITCH_OVER_TEMP].alarm_desc = "STATIC_SWITCH_OVER_TEMP";
523 	bcmxcp_alarm_map[BCMXCP_ALARM_CHARGER_OVER_TEMP].alarm_desc = "CHARGER_OVER_TEMP";
524 	bcmxcp_alarm_map[BCMXCP_ALARM_CHARGER_LOGIC_PWR_FAIL].alarm_desc = "CHARGER_LOGIC_PWR_FAIL";
525 	bcmxcp_alarm_map[BCMXCP_ALARM_CHARGER_OVER_VOLTAGE_OR_CURRENT].alarm_desc = "CHARGER_OVER_VOLTAGE_OR_CURRENT";
526 	bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_OVER_TEMP].alarm_desc = "INVERTER_OVER_TEMP";
527 	bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_OVERLOAD].alarm_desc = "OUTPUT_OVERLOAD";
528 	bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_INPUT_OVER_CURRENT].alarm_desc = "RECTIFIER_INPUT_OVER_CURRENT";
529 	bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_OUTPUT_OVER_CURRENT].alarm_desc = "INVERTER_OUTPUT_OVER_CURRENT";
530 	bcmxcp_alarm_map[BCMXCP_ALARM_DC_LINK_OVER_VOLTAGE].alarm_desc = "DC_LINK_OVER_VOLTAGE";
531 	bcmxcp_alarm_map[BCMXCP_ALARM_DC_LINK_UNDER_VOLTAGE].alarm_desc = "DC_LINK_UNDER_VOLTAGE";
532 	bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_FAILED].alarm_desc = "RECTIFIER_FAILED";
533 	bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_FAULT].alarm_desc = "INVERTER_FAULT";
534 	bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_CONNECTOR_FAIL].alarm_desc = "BATTERY_CONNECTOR_FAIL";
535 	bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_BREAKER_FAIL].alarm_desc = "BYPASS_BREAKER_FAIL";
536 	bcmxcp_alarm_map[BCMXCP_ALARM_CHARGER_FAIL].alarm_desc = "CHARGER_FAIL";
537 	bcmxcp_alarm_map[BCMXCP_ALARM_RAMP_UP_FAILED].alarm_desc = "RAMP_UP_FAILED";
538 	bcmxcp_alarm_map[BCMXCP_ALARM_STATIC_SWITCH_FAILED].alarm_desc = "STATIC_SWITCH_FAILED";
539 	bcmxcp_alarm_map[BCMXCP_ALARM_ANALOG_AD_REF_FAIL].alarm_desc = "ANALOG_AD_REF_FAIL";
540 	bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_UNCALIBRATED].alarm_desc = "BYPASS_UNCALIBRATED";
541 	bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_UNCALIBRATED].alarm_desc = "RECTIFIER_UNCALIBRATED";
542 	bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_UNCALIBRATED].alarm_desc = "OUTPUT_UNCALIBRATED";
543 	bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_UNCALIBRATED].alarm_desc = "INVERTER_UNCALIBRATED";
544 	bcmxcp_alarm_map[BCMXCP_ALARM_DC_VOLT_UNCALIBRATED].alarm_desc = "DC_VOLT_UNCALIBRATED";
545 	bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_CURRENT_UNCALIBRATED].alarm_desc = "OUTPUT_CURRENT_UNCALIBRATED";
546 	bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_CURRENT_UNCALIBRATED].alarm_desc = "RECTIFIER_CURRENT_UNCALIBRATED";
547 	bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_CURRENT_UNCALIBRATED].alarm_desc = "BATTERY_CURRENT_UNCALIBRATED";
548 	bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_ON_OFF_STAT_FAIL].alarm_desc = "INVERTER_ON_OFF_STAT_FAIL";
549 	bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_CURRENT_LIMIT].alarm_desc = "BATTERY_CURRENT_LIMIT";
550 	bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_STARTUP_FAIL].alarm_desc = "INVERTER_STARTUP_FAIL";
551 	bcmxcp_alarm_map[BCMXCP_ALARM_ANALOG_BOARD_AD_STAT_FAIL].alarm_desc = "ANALOG_BOARD_AD_STAT_FAIL";
552 	bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_CURRENT_OVER_100].alarm_desc = "OUTPUT_CURRENT_OVER_100";
553 	bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_GROUND_FAULT].alarm_desc = "BATTERY_GROUND_FAULT";
554 	bcmxcp_alarm_map[BCMXCP_ALARM_WAITING_FOR_CHARGER_SYNC].alarm_desc = "WAITING_FOR_CHARGER_SYNC";
555 	bcmxcp_alarm_map[BCMXCP_ALARM_NV_RAM_FAIL].alarm_desc = "NV_RAM_FAIL";
556 	bcmxcp_alarm_map[BCMXCP_ALARM_ANALOG_BOARD_AD_TIMEOUT].alarm_desc = "ANALOG_BOARD_AD_TIMEOUT";
557 	bcmxcp_alarm_map[BCMXCP_ALARM_SHUTDOWN_IMMINENT].alarm_desc = "SHUTDOWN_IMMINENT";
558 	bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_LOW].alarm_desc = "BATTERY_LOW";
559 	bcmxcp_alarm_map[BCMXCP_ALARM_UTILITY_FAIL].alarm_desc = "UTILITY_FAIL";
560 	bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_SHORT_CIRCUIT].alarm_desc = "OUTPUT_SHORT_CIRCUIT";
561 	bcmxcp_alarm_map[BCMXCP_ALARM_UTILITY_NOT_PRESENT].alarm_desc = "UTILITY_NOT_PRESENT";
562 	bcmxcp_alarm_map[BCMXCP_ALARM_FULL_TIME_CHARGING].alarm_desc = "FULL_TIME_CHARGING";
563 	bcmxcp_alarm_map[BCMXCP_ALARM_FAST_BYPASS_COMMAND].alarm_desc = "FAST_BYPASS_COMMAND";
564 	bcmxcp_alarm_map[BCMXCP_ALARM_AD_ERROR].alarm_desc = "AD_ERROR";
565 	bcmxcp_alarm_map[BCMXCP_ALARM_INTERNAL_COM_FAIL].alarm_desc = "INTERNAL_COM_FAIL";
566 	bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_SELFTEST_FAIL].alarm_desc = "RECTIFIER_SELFTEST_FAIL";
567 	bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_EEPROM_FAIL].alarm_desc = "RECTIFIER_EEPROM_FAIL";
568 	bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_EPROM_FAIL].alarm_desc = "RECTIFIER_EPROM_FAIL";
569 	bcmxcp_alarm_map[BCMXCP_ALARM_INPUT_LINE_VOLTAGE_LOSS].alarm_desc = "INPUT_LINE_VOLTAGE_LOSS";
570 	bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_DC_OVER_VOLTAGE].alarm_desc = "BATTERY_DC_OVER_VOLTAGE";
571 	bcmxcp_alarm_map[BCMXCP_ALARM_POWER_SUPPLY_OVER_TEMP].alarm_desc = "POWER_SUPPLY_OVER_TEMP";
572 	bcmxcp_alarm_map[BCMXCP_ALARM_POWER_SUPPLY_FAIL].alarm_desc = "POWER_SUPPLY_FAIL";
573 	bcmxcp_alarm_map[BCMXCP_ALARM_POWER_SUPPLY_5V_FAIL].alarm_desc = "POWER_SUPPLY_5V_FAIL";
574 	bcmxcp_alarm_map[BCMXCP_ALARM_POWER_SUPPLY_12V_FAIL].alarm_desc = "POWER_SUPPLY_12V_FAIL";
575 	bcmxcp_alarm_map[BCMXCP_ALARM_HEATSINK_OVER_TEMP].alarm_desc = "HEATSINK_OVER_TEMP";
576 	bcmxcp_alarm_map[BCMXCP_ALARM_HEATSINK_TEMP_SENSOR_FAIL].alarm_desc = "HEATSINK_TEMP_SENSOR_FAIL";
577 	bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_CURRENT_OVER_125].alarm_desc = "RECTIFIER_CURRENT_OVER_125";
578 	bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_FAULT_INTERRUPT_FAIL].alarm_desc = "RECTIFIER_FAULT_INTERRUPT_FAIL";
579 	bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_POWER_CAPASITOR_FAIL].alarm_desc = "RECTIFIER_POWER_CAPASITOR_FAIL";
580 	bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_PROGRAM_STACK_ERROR].alarm_desc = "INVERTER_PROGRAM_STACK_ERROR";
581 	bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_BOARD_SELFTEST_FAIL].alarm_desc = "INVERTER_BOARD_SELFTEST_FAIL";
582 	bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_AD_SELFTEST_FAIL].alarm_desc = "INVERTER_AD_SELFTEST_FAIL";
583 	bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_RAM_SELFTEST_FAIL].alarm_desc = "INVERTER_RAM_SELFTEST_FAIL";
584 	bcmxcp_alarm_map[BCMXCP_ALARM_NV_MEMORY_CHECKSUM_FAIL].alarm_desc = "NV_MEMORY_CHECKSUM_FAIL";
585 	bcmxcp_alarm_map[BCMXCP_ALARM_PROGRAM_CHECKSUM_FAIL].alarm_desc = "PROGRAM_CHECKSUM_FAIL";
586 	bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_CPU_SELFTEST_FAIL].alarm_desc = "INVERTER_CPU_SELFTEST_FAIL";
587 	bcmxcp_alarm_map[BCMXCP_ALARM_NETWORK_NOT_RESPONDING].alarm_desc = "NETWORK_NOT_RESPONDING";
588 	bcmxcp_alarm_map[BCMXCP_ALARM_FRONT_PANEL_SELFTEST_FAIL].alarm_desc = "FRONT_PANEL_SELFTEST_FAIL";
589 	bcmxcp_alarm_map[BCMXCP_ALARM_NODE_EEPROM_VERIFICATION_ERROR].alarm_desc = "NODE_EEPROM_VERIFICATION_ERROR";
590 	bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_AC_OVER_VOLT_TEST_FAIL].alarm_desc = "OUTPUT_AC_OVER_VOLT_TEST_FAIL";
591 	bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_DC_OVER_VOLTAGE].alarm_desc = "OUTPUT_DC_OVER_VOLTAGE";
592 	bcmxcp_alarm_map[BCMXCP_ALARM_INPUT_PHASE_ROTATION_ERROR].alarm_desc = "INPUT_PHASE_ROTATION_ERROR";
593 	bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_RAMP_UP_TEST_FAILED].alarm_desc = "INVERTER_RAMP_UP_TEST_FAILED";
594 	bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_OFF_COMMAND].alarm_desc = "INVERTER_OFF_COMMAND";
595 	bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_ON_COMMAND].alarm_desc = "INVERTER_ON_COMMAND";
596 	bcmxcp_alarm_map[BCMXCP_ALARM_TO_BYPASS_COMMAND].alarm_desc = "TO_BYPASS_COMMAND";
597 	bcmxcp_alarm_map[BCMXCP_ALARM_FROM_BYPASS_COMMAND].alarm_desc = "FROM_BYPASS_COMMAND";
598 	bcmxcp_alarm_map[BCMXCP_ALARM_AUTO_MODE_COMMAND].alarm_desc = "AUTO_MODE_COMMAND";
599 	bcmxcp_alarm_map[BCMXCP_ALARM_EMERGENCY_SHUTDOWN_COMMAND].alarm_desc = "EMERGENCY_SHUTDOWN_COMMAND";
600 	bcmxcp_alarm_map[BCMXCP_ALARM_SETUP_SWITCH_OPEN].alarm_desc = "SETUP_SWITCH_OPEN";
601 	bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_OVER_VOLT_INT].alarm_desc = "INVERTER_OVER_VOLT_INT";
602 	bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_UNDER_VOLT_INT].alarm_desc = "INVERTER_UNDER_VOLT_INT";
603 	bcmxcp_alarm_map[BCMXCP_ALARM_ABSOLUTE_DCOV_ACOV].alarm_desc = "ABSOLUTE_DCOV_ACOV";
604 	bcmxcp_alarm_map[BCMXCP_ALARM_PHASE_A_CURRENT_LIMIT].alarm_desc = "PHASE_A_CURRENT_LIMIT";
605 	bcmxcp_alarm_map[BCMXCP_ALARM_PHASE_B_CURRENT_LIMIT].alarm_desc = "PHASE_B_CURRENT_LIMIT";
606 	bcmxcp_alarm_map[BCMXCP_ALARM_PHASE_C_CURRENT_LIMIT].alarm_desc = "PHASE_C_CURRENT_LIMIT";
607 	bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_NOT_AVAILABLE].alarm_desc = "BYPASS_NOT_AVAILABLE";
608 	bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_BREAKER_OPEN].alarm_desc = "RECTIFIER_BREAKER_OPEN";
609 	bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_CONTACTOR_OPEN].alarm_desc = "BATTERY_CONTACTOR_OPEN";
610 	bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_CONTACTOR_OPEN].alarm_desc = "INVERTER_CONTACTOR_OPEN";
611 	bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_BREAKER_OPEN].alarm_desc = "BYPASS_BREAKER_OPEN";
612 	bcmxcp_alarm_map[BCMXCP_ALARM_INV_BOARD_ACOV_INT_TEST_FAIL].alarm_desc = "INV_BOARD_ACOV_INT_TEST_FAIL";
613 	bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_OVER_TEMP_TRIP].alarm_desc = "INVERTER_OVER_TEMP_TRIP";
614 	bcmxcp_alarm_map[BCMXCP_ALARM_INV_BOARD_ACUV_INT_TEST_FAIL].alarm_desc = "INV_BOARD_ACUV_INT_TEST_FAIL";
615 	bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_VOLTAGE_FEEDBACK_ERROR].alarm_desc = "INVERTER_VOLTAGE_FEEDBACK_ERROR";
616 	bcmxcp_alarm_map[BCMXCP_ALARM_DC_UNDER_VOLTAGE_TIMEOUT].alarm_desc = "DC_UNDER_VOLTAGE_TIMEOUT";
617 	bcmxcp_alarm_map[BCMXCP_ALARM_AC_UNDER_VOLTAGE_TIMEOUT].alarm_desc = "AC_UNDER_VOLTAGE_TIMEOUT";
618 	bcmxcp_alarm_map[BCMXCP_ALARM_DC_UNDER_VOLTAGE_WHILE_CHARGE].alarm_desc = "DC_UNDER_VOLTAGE_WHILE_CHARGE";
619 	bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_VOLTAGE_BIAS_ERROR].alarm_desc = "INVERTER_VOLTAGE_BIAS_ERROR";
620 	bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_PHASE_ROTATION].alarm_desc = "RECTIFIER_PHASE_ROTATION";
621 	bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_PHASER_ROTATION].alarm_desc = "BYPASS_PHASER_ROTATION";
622 	bcmxcp_alarm_map[BCMXCP_ALARM_SYSTEM_INTERFACE_BOARD_FAIL].alarm_desc = "SYSTEM_INTERFACE_BOARD_FAIL";
623 	bcmxcp_alarm_map[BCMXCP_ALARM_PARALLEL_BOARD_FAIL].alarm_desc = "PARALLEL_BOARD_FAIL";
624 	bcmxcp_alarm_map[BCMXCP_ALARM_LOST_LOAD_SHARING_PHASE_A].alarm_desc = "LOST_LOAD_SHARING_PHASE_A";
625 	bcmxcp_alarm_map[BCMXCP_ALARM_LOST_LOAD_SHARING_PHASE_B].alarm_desc = "LOST_LOAD_SHARING_PHASE_B";
626 	bcmxcp_alarm_map[BCMXCP_ALARM_LOST_LOAD_SHARING_PHASE_C].alarm_desc = "LOST_LOAD_SHARING_PHASE_C";
627 	bcmxcp_alarm_map[BCMXCP_ALARM_DC_OVER_VOLTAGE_TIMEOUT].alarm_desc = "DC_OVER_VOLTAGE_TIMEOUT";
628 	bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_TOTALLY_DISCHARGED].alarm_desc = "BATTERY_TOTALLY_DISCHARGED";
629 	bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_PHASE_BIAS_ERROR].alarm_desc = "INVERTER_PHASE_BIAS_ERROR";
630 	bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_VOLTAGE_BIAS_ERROR_2].alarm_desc = "INVERTER_VOLTAGE_BIAS_ERROR_2";
631 	bcmxcp_alarm_map[BCMXCP_ALARM_DC_LINK_BLEED_COMPLETE].alarm_desc = "DC_LINK_BLEED_COMPLETE";
632 	bcmxcp_alarm_map[BCMXCP_ALARM_LARGE_CHARGER_INPUT_CURRENT].alarm_desc = "LARGE_CHARGER_INPUT_CURRENT";
633 	bcmxcp_alarm_map[BCMXCP_ALARM_INV_VOLT_TOO_LOW_FOR_RAMP_LEVEL].alarm_desc = "INV_VOLT_TOO_LOW_FOR_RAMP_LEVEL";
634 	bcmxcp_alarm_map[BCMXCP_ALARM_LOSS_OF_REDUNDANCY].alarm_desc = "LOSS_OF_REDUNDANCY";
635 	bcmxcp_alarm_map[BCMXCP_ALARM_LOSS_OF_SYNC_BUS].alarm_desc = "LOSS_OF_SYNC_BUS";
636 	bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_BREAKER_SHUNT_TRIP].alarm_desc = "RECTIFIER_BREAKER_SHUNT_TRIP";
637 	bcmxcp_alarm_map[BCMXCP_ALARM_LOSS_OF_CHARGER_SYNC].alarm_desc = "LOSS_OF_CHARGER_SYNC";
638 	bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_LOW_LEVEL_TEST_TIMEOUT].alarm_desc = "INVERTER_LOW_LEVEL_TEST_TIMEOUT";
639 	bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_BREAKER_OPEN].alarm_desc = "OUTPUT_BREAKER_OPEN";
640 	bcmxcp_alarm_map[BCMXCP_ALARM_CONTROL_POWER_ON].alarm_desc = "CONTROL_POWER_ON";
641 	bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_ON].alarm_desc = "INVERTER_ON";
642 	bcmxcp_alarm_map[BCMXCP_ALARM_CHARGER_ON].alarm_desc = "CHARGER_ON";
643 	bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_ON].alarm_desc = "BYPASS_ON";
644 	bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_POWER_LOSS].alarm_desc = "BYPASS_POWER_LOSS";
645 	bcmxcp_alarm_map[BCMXCP_ALARM_ON_MANUAL_BYPASS].alarm_desc = "ON_MANUAL_BYPASS";
646 	bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_MANUAL_TURN_OFF].alarm_desc = "BYPASS_MANUAL_TURN_OFF";
647 	bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_BLEEDING_DC_LINK_VOLT].alarm_desc = "INVERTER_BLEEDING_DC_LINK_VOLT";
648 	bcmxcp_alarm_map[BCMXCP_ALARM_CPU_ISR_ERROR].alarm_desc = "CPU_ISR_ERROR";
649 	bcmxcp_alarm_map[BCMXCP_ALARM_SYSTEM_ISR_RESTART].alarm_desc = "SYSTEM_ISR_RESTART";
650 	bcmxcp_alarm_map[BCMXCP_ALARM_PARALLEL_DC].alarm_desc = "PARALLEL_DC";
651 	bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_NEEDS_SERVICE].alarm_desc = "BATTERY_NEEDS_SERVICE";
652 	bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_CHARGING].alarm_desc = "BATTERY_CHARGING";
653 	bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_NOT_CHARGED].alarm_desc = "BATTERY_NOT_CHARGED";
654 	bcmxcp_alarm_map[BCMXCP_ALARM_DISABLED_BATTERY_TIME].alarm_desc = "DISABLED_BATTERY_TIME";
655 	bcmxcp_alarm_map[BCMXCP_ALARM_SERIES_7000_ENABLE].alarm_desc = "SERIES_7000_ENABLE";
656 	bcmxcp_alarm_map[BCMXCP_ALARM_OTHER_UPS_ON].alarm_desc = "OTHER_UPS_ON";
657 	bcmxcp_alarm_map[BCMXCP_ALARM_PARALLEL_INVERTER].alarm_desc = "PARALLEL_INVERTER";
658 	bcmxcp_alarm_map[BCMXCP_ALARM_UPS_IN_PARALLEL].alarm_desc = "UPS_IN_PARALLEL";
659 	bcmxcp_alarm_map[BCMXCP_ALARM_OUTPUT_BREAKER_REALY_FAIL].alarm_desc = "OUTPUT_BREAKER_REALY_FAIL";
660 	bcmxcp_alarm_map[BCMXCP_ALARM_CONTROL_POWER_OFF].alarm_desc = "CONTROL_POWER_OFF";
661 	bcmxcp_alarm_map[BCMXCP_ALARM_LEVEL_2_OVERLOAD_PHASE_A].alarm_desc = "LEVEL_2_OVERLOAD_PHASE_A";
662 	bcmxcp_alarm_map[BCMXCP_ALARM_LEVEL_2_OVERLOAD_PHASE_B].alarm_desc = "LEVEL_2_OVERLOAD_PHASE_B";
663 	bcmxcp_alarm_map[BCMXCP_ALARM_LEVEL_2_OVERLOAD_PHASE_C].alarm_desc = "LEVEL_2_OVERLOAD_PHASE_C";
664 	bcmxcp_alarm_map[BCMXCP_ALARM_LEVEL_3_OVERLOAD_PHASE_A].alarm_desc = "LEVEL_3_OVERLOAD_PHASE_A";
665 	bcmxcp_alarm_map[BCMXCP_ALARM_LEVEL_3_OVERLOAD_PHASE_B].alarm_desc = "LEVEL_3_OVERLOAD_PHASE_B";
666 	bcmxcp_alarm_map[BCMXCP_ALARM_LEVEL_3_OVERLOAD_PHASE_C].alarm_desc = "LEVEL_3_OVERLOAD_PHASE_C";
667 	bcmxcp_alarm_map[BCMXCP_ALARM_LEVEL_4_OVERLOAD_PHASE_A].alarm_desc = "LEVEL_4_OVERLOAD_PHASE_A";
668 	bcmxcp_alarm_map[BCMXCP_ALARM_LEVEL_4_OVERLOAD_PHASE_B].alarm_desc = "LEVEL_4_OVERLOAD_PHASE_B";
669 	bcmxcp_alarm_map[BCMXCP_ALARM_LEVEL_4_OVERLOAD_PHASE_C].alarm_desc = "LEVEL_4_OVERLOAD_PHASE_C";
670 	bcmxcp_alarm_map[BCMXCP_ALARM_UPS_ON_BATTERY].alarm_desc = "UPS_ON_BATTERY";
671 	bcmxcp_alarm_map[BCMXCP_ALARM_UPS_ON_BYPASS].alarm_desc = "UPS_ON_BYPASS";
672 	bcmxcp_alarm_map[BCMXCP_ALARM_LOAD_DUMPED].alarm_desc = "LOAD_DUMPED";
673 	bcmxcp_alarm_map[BCMXCP_ALARM_LOAD_ON_INVERTER].alarm_desc = "LOAD_ON_INVERTER";
674 	bcmxcp_alarm_map[BCMXCP_ALARM_UPS_ON_COMMAND].alarm_desc = "UPS_ON_COMMAND";
675 	bcmxcp_alarm_map[BCMXCP_ALARM_UPS_OFF_COMMAND].alarm_desc = "UPS_OFF_COMMAND";
676 	bcmxcp_alarm_map[BCMXCP_ALARM_LOW_BATTERY_SHUTDOWN].alarm_desc = "LOW_BATTERY_SHUTDOWN";
677 	bcmxcp_alarm_map[BCMXCP_ALARM_AUTO_ON_ENABLED].alarm_desc = "AUTO_ON_ENABLED";
678 	bcmxcp_alarm_map[BCMXCP_ALARM_SOFTWARE_INCOMPABILITY_DETECTED].alarm_desc = "SOFTWARE_INCOMPABILITY_DETECTED";
679 	bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_TEMP_SENSOR_FAILED].alarm_desc = "INVERTER_TEMP_SENSOR_FAILED";
680 	bcmxcp_alarm_map[BCMXCP_ALARM_DC_START_OCCURED].alarm_desc = "DC_START_OCCURED";
681 	bcmxcp_alarm_map[BCMXCP_ALARM_IN_PARALLEL_OPERATION].alarm_desc = "IN_PARALLEL_OPERATION";
682 	bcmxcp_alarm_map[BCMXCP_ALARM_SYNCING_TO_BYPASS].alarm_desc = "SYNCING_TO_BYPASS";
683 	bcmxcp_alarm_map[BCMXCP_ALARM_RAMPING_UPS_UP].alarm_desc = "RAMPING_UPS_UP";
684 	bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_ON_DELAY].alarm_desc = "INVERTER_ON_DELAY";
685 	bcmxcp_alarm_map[BCMXCP_ALARM_CHARGER_ON_DELAY].alarm_desc = "CHARGER_ON_DELAY";
686 	bcmxcp_alarm_map[BCMXCP_ALARM_WAITING_FOR_UTIL_INPUT].alarm_desc = "WAITING_FOR_UTIL_INPUT";
687 	bcmxcp_alarm_map[BCMXCP_ALARM_CLOSE_BYPASS_BREAKER].alarm_desc = "CLOSE_BYPASS_BREAKER";
688 	bcmxcp_alarm_map[BCMXCP_ALARM_TEMPORARY_BYPASS_OPERATION].alarm_desc = "TEMPORARY_BYPASS_OPERATION";
689 	bcmxcp_alarm_map[BCMXCP_ALARM_SYNCING_TO_OUTPUT].alarm_desc = "SYNCING_TO_OUTPUT";
690 	bcmxcp_alarm_map[BCMXCP_ALARM_BYPASS_FAILURE].alarm_desc = "BYPASS_FAILURE";
691 	bcmxcp_alarm_map[BCMXCP_ALARM_AUTO_OFF_COMMAND_EXECUTED].alarm_desc = "AUTO_OFF_COMMAND_EXECUTED";
692 	bcmxcp_alarm_map[BCMXCP_ALARM_AUTO_ON_COMMAND_EXECUTED].alarm_desc = "AUTO_ON_COMMAND_EXECUTED";
693 	bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_TEST_FAILED].alarm_desc = "BATTERY_TEST_FAILED";
694 	bcmxcp_alarm_map[BCMXCP_ALARM_FUSE_FAIL].alarm_desc = "FUSE_FAIL";
695 	bcmxcp_alarm_map[BCMXCP_ALARM_FAN_FAIL].alarm_desc = "FAN_FAIL";
696 	bcmxcp_alarm_map[BCMXCP_ALARM_SITE_WIRING_FAULT].alarm_desc = "SITE_WIRING_FAULT";
697 	bcmxcp_alarm_map[BCMXCP_ALARM_BACKFEED_CONTACTOR_FAIL].alarm_desc = "BACKFEED_CONTACTOR_FAIL";
698 	bcmxcp_alarm_map[BCMXCP_ALARM_ON_BUCK].alarm_desc = "ON_BUCK";
699 	bcmxcp_alarm_map[BCMXCP_ALARM_ON_BOOST].alarm_desc = "ON_BOOST";
700 	bcmxcp_alarm_map[BCMXCP_ALARM_ON_DOUBLE_BOOST].alarm_desc = "ON_DOUBLE_BOOST";
701 	bcmxcp_alarm_map[BCMXCP_ALARM_BATTERIES_DISCONNECTED].alarm_desc = "BATTERIES_DISCONNECTED";
702 	bcmxcp_alarm_map[BCMXCP_ALARM_UPS_CABINET_OVER_TEMP].alarm_desc = "UPS_CABINET_OVER_TEMP";
703 	bcmxcp_alarm_map[BCMXCP_ALARM_TRANSFORMER_OVER_TEMP].alarm_desc = "TRANSFORMER_OVER_TEMP";
704 	bcmxcp_alarm_map[BCMXCP_ALARM_AMBIENT_UNDER_TEMP].alarm_desc = "AMBIENT_UNDER_TEMP";
705 	bcmxcp_alarm_map[BCMXCP_ALARM_AMBIENT_OVER_TEMP].alarm_desc = "AMBIENT_OVER_TEMP";
706 	bcmxcp_alarm_map[BCMXCP_ALARM_CABINET_DOOR_OPEN].alarm_desc = "CABINET_DOOR_OPEN";
707 	bcmxcp_alarm_map[BCMXCP_ALARM_CABINET_DOOR_OPEN_VOLT_PRESENT].alarm_desc = "CABINET_DOOR_OPEN_VOLT_PRESENT";
708 	bcmxcp_alarm_map[BCMXCP_ALARM_AUTO_SHUTDOWN_PENDING].alarm_desc = "AUTO_SHUTDOWN_PENDING";
709 	bcmxcp_alarm_map[BCMXCP_ALARM_TAP_SWITCHING_REALY_PENDING].alarm_desc = "TAP_SWITCHING_REALY_PENDING";
710 	bcmxcp_alarm_map[BCMXCP_ALARM_UNABLE_TO_CHARGE_BATTERIES].alarm_desc = "UNABLE_TO_CHARGE_BATTERIES";
711 	bcmxcp_alarm_map[BCMXCP_ALARM_STARTUP_FAILURE_CHECK_EPO].alarm_desc = "STARTUP_FAILURE_CHECK_EPO";
712 	bcmxcp_alarm_map[BCMXCP_ALARM_AUTOMATIC_STARTUP_PENDING].alarm_desc = "AUTOMATIC_STARTUP_PENDING";
713 	bcmxcp_alarm_map[BCMXCP_ALARM_MODEM_FAILED].alarm_desc = "MODEM_FAILED";
714 	bcmxcp_alarm_map[BCMXCP_ALARM_INCOMING_MODEM_CALL_STARTED].alarm_desc = "INCOMING_MODEM_CALL_STARTED";
715 	bcmxcp_alarm_map[BCMXCP_ALARM_OUTGOING_MODEM_CALL_STARTED].alarm_desc = "OUTGOING_MODEM_CALL_STARTED";
716 	bcmxcp_alarm_map[BCMXCP_ALARM_MODEM_CONNECTION_ESTABLISHED].alarm_desc = "MODEM_CONNECTION_ESTABLISHED";
717 	bcmxcp_alarm_map[BCMXCP_ALARM_MODEM_CALL_COMPLETED_SUCCESS].alarm_desc = "MODEM_CALL_COMPLETED_SUCCESS";
718 	bcmxcp_alarm_map[BCMXCP_ALARM_MODEM_CALL_COMPLETED_FAIL].alarm_desc = "MODEM_CALL_COMPLETED_FAIL";
719 	bcmxcp_alarm_map[BCMXCP_ALARM_INPUT_BREAKER_FAIL].alarm_desc = "INPUT_BREAKER_FAIL";
720 	bcmxcp_alarm_map[BCMXCP_ALARM_SYSINIT_IN_PROGRESS].alarm_desc = "SYSINIT_IN_PROGRESS";
721 	bcmxcp_alarm_map[BCMXCP_ALARM_AUTOCALIBRATION_FAIL].alarm_desc = "AUTOCALIBRATION_FAIL";
722 	bcmxcp_alarm_map[BCMXCP_ALARM_SELECTIVE_TRIP_OF_MODULE].alarm_desc = "SELECTIVE_TRIP_OF_MODULE";
723 	bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_OUTPUT_FAILURE].alarm_desc = "INVERTER_OUTPUT_FAILURE";
724 	bcmxcp_alarm_map[BCMXCP_ALARM_ABNORMAL_OUTPUT_VOLT_AT_STARTUP].alarm_desc = "ABNORMAL_OUTPUT_VOLT_AT_STARTUP";
725 	bcmxcp_alarm_map[BCMXCP_ALARM_RECTIFIER_OVER_TEMP].alarm_desc = "RECTIFIER_OVER_TEMP";
726 	bcmxcp_alarm_map[BCMXCP_ALARM_CONFIG_ERROR].alarm_desc = "CONFIG_ERROR";
727 	bcmxcp_alarm_map[BCMXCP_ALARM_REDUNDANCY_LOSS_DUE_TO_OVERLOAD].alarm_desc = "REDUNDANCY_LOSS_DUE_TO_OVERLOAD";
728 	bcmxcp_alarm_map[BCMXCP_ALARM_ON_ALTERNATE_AC_SOURCE].alarm_desc = "ON_ALTERNATE_AC_SOURCE";
729 	bcmxcp_alarm_map[BCMXCP_ALARM_IN_HIGH_EFFICIENCY_MODE].alarm_desc = "IN_HIGH_EFFICIENCY_MODE";
730 	bcmxcp_alarm_map[BCMXCP_ALARM_SYSTEM_NOTICE_ACTIVE].alarm_desc = "SYSTEM_NOTICE_ACTIVE";
731 	bcmxcp_alarm_map[BCMXCP_ALARM_SYSTEM_ALARM_ACTIVE].alarm_desc = "SYSTEM_ALARM_ACTIVE";
732 	bcmxcp_alarm_map[BCMXCP_ALARM_ALTERNATE_POWER_SOURCE_NOT_AVAILABLE].alarm_desc = "ALTERNATE_POWER_SOURCE_NOT_AVAILABLE";
733 	bcmxcp_alarm_map[BCMXCP_ALARM_CURRENT_BALANCE_FAILURE].alarm_desc = "CURRENT_BALANCE_FAILURE";
734 	bcmxcp_alarm_map[BCMXCP_ALARM_CHECK_AIR_FILTER].alarm_desc = "CHECK_AIR_FILTER";
735 	bcmxcp_alarm_map[BCMXCP_ALARM_SUBSYSTEM_NOTICE_ACTIVE].alarm_desc = "SUBSYSTEM_NOTICE_ACTIVE";
736 	bcmxcp_alarm_map[BCMXCP_ALARM_SUBSYSTEM_ALARM_ACTIVE].alarm_desc = "SUBSYSTEM_ALARM_ACTIVE";
737 	bcmxcp_alarm_map[BCMXCP_ALARM_CHARGER_ON_COMMAND].alarm_desc = "CHARGER_ON_COMMAND";
738 	bcmxcp_alarm_map[BCMXCP_ALARM_CHARGER_OFF_COMMAND].alarm_desc = "CHARGER_OFF_COMMAND";
739 	bcmxcp_alarm_map[BCMXCP_ALARM_UPS_NORMAL].alarm_desc = "UPS_NORMAL";
740 	bcmxcp_alarm_map[BCMXCP_ALARM_INVERTER_PHASE_ROTATION].alarm_desc = "INVERTER_PHASE_ROTATION";
741 	bcmxcp_alarm_map[BCMXCP_ALARM_UPS_OFF].alarm_desc = "UPS_OFF";
742 	bcmxcp_alarm_map[BCMXCP_ALARM_EXTERNAL_COMMUNICATION_FAILURE].alarm_desc = "EXTERNAL_COMMUNICATION_FAILURE";
743 	bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_TEST_INPROGRESS].alarm_desc = "BATTERY_TEST_INPROGRESS";
744 	bcmxcp_alarm_map[BCMXCP_ALARM_SYSTEM_TEST_INPROGRESS].alarm_desc = "SYSTEM_TEST_INPROGRESS";
745 	bcmxcp_alarm_map[BCMXCP_ALARM_BATTERY_TEST_ABORTED].alarm_desc = "BATTERY_TEST_ABORTED";
746 }
747 
748 /* Get information on UPS commands */
init_command(int size)749 bool_t init_command(int size)
750 {
751 	unsigned char answer[PW_ANSWER_MAX_SIZE];
752 	unsigned char commandByte;
753 	const char* nutvalue;
754 	int res, iIndex = 0, ncounter, NumComms = 0, i;
755 
756 	upsdebugx(1, "entering init_command(%i)", size);
757 
758 	res = command_read_sequence(PW_COMMAND_LIST_REQ, answer);
759 	if (res <= 0)
760 	{
761 		upsdebugx(2, "No command list block.");
762 		return FALSE;
763 	}
764 	else
765 	{
766 		upsdebugx(2, "Command list block supported.");
767 
768 		res = answer[iIndex];
769 		NumComms = (int)res; /* Number of commands implemented in this UPS */
770 		upsdebugx(3, "Number of commands implemented in ups %d", res);
771 		iIndex++;
772 		res = answer[iIndex]; /* Entry length - bytes reported for each command */
773 		iIndex++;
774 		upsdebugx(5, "bytes per command %d", res);
775 
776 		/* In case of debug - make explanation of values */
777 		upsdebugx(2, "Index\tCmd byte\tDescription");
778 
779 		/* Get command bytes if size of command block matches with size from standard ID block */
780 		if (NumComms + 2 == size)
781 		{
782 			for (ncounter = 0; ncounter < NumComms; ncounter++)
783 			{
784 				commandByte = answer[iIndex];
785 				if(commandByte >= 0 && commandByte < BCMXCP_COMMAND_MAP_MAX) {
786 					upsdebugx(2, "%03d\t%02x\t%s", ncounter, commandByte, bcmxcp_command_map[commandByte].command_desc);
787 					bcmxcp_command_map[commandByte].command_byte = commandByte;
788 				}
789 				else {
790 					upsdebugx(2, "%03d\t%02x\t%s", ncounter, commandByte, "Unknown command, the commandByte is not mapped");
791 				}
792 
793 				iIndex++;
794 			}
795 
796 			/* Map supported commands to instcmd */
797 			for(i = 0; i < BCMXCP_COMMAND_MAP_MAX; i++) {
798 				if(bcmxcp_command_map[i].command_desc != NULL) {
799 					if(bcmxcp_command_map[i].command_byte > 0) {
800 						if ((nutvalue = nut_find_infoval(command_map_info, bcmxcp_command_map[i].command_byte, FALSE)) != NULL) {
801 							dstate_addcmd(nutvalue);
802 							upsdebugx(2, "Added support for instcmd %s", nutvalue);
803 						}
804 					}
805 				}
806 			}
807 
808 			return TRUE;
809 		}
810 		else {
811 			upsdebugx(1, "Invalid response received from Command List block");
812 			return FALSE;
813 		}
814 	}
815 }
816 
init_ups_meter_map(const unsigned char * map,unsigned char len)817 void init_ups_meter_map(const unsigned char *map, unsigned char len)
818 {
819 	unsigned int iIndex, iOffset = 0;
820 
821 	/* In case of debug - make explanation of values */
822 	upsdebugx(2, "Index\tOffset\tFormat\tNUT");
823 
824 	/* Loop thru map */
825 	for (iIndex = 0; iIndex < len && iIndex < BCMXCP_METER_MAP_MAX; iIndex++)
826 	{
827 		bcmxcp_meter_map[iIndex].format = map[iIndex];
828 		if (map[iIndex] != 0)
829 		{
830 			/* Set meter map entry offset */
831 			bcmxcp_meter_map[iIndex].meter_block_index = iOffset;
832 
833 			/* Debug info */
834 			upsdebugx(2, "%04d\t%04d\t%2x\t%s", iIndex, iOffset, bcmxcp_meter_map[iIndex].format,
835 					(bcmxcp_meter_map[iIndex].nut_entity == NULL ? "None" :bcmxcp_meter_map[iIndex].nut_entity));
836 
837 			iOffset += 4;
838 		}
839 	}
840 	upsdebugx(2, "\n");
841 }
842 
decode_meter_map_entry(const unsigned char * entry,const unsigned char format,char * value)843 void decode_meter_map_entry(const unsigned char *entry, const unsigned char format, char* value)
844 {
845 	long lValue = 0;
846 	char sFormat[32];
847 	float fValue;
848 	unsigned char dd, mm, yy, cc, hh, ss;
849 
850 	/* Paranoid input sanity checks */
851 	if (value == NULL)
852 		return;
853 	*value = '\0';
854 	if (entry == (unsigned char *)NULL || format == 0x00)
855 		return;
856 
857 	/* Get data based on format */
858 	if (format == 0xf0) {
859 		/* Long integer */
860 		lValue = get_long(entry);
861 		snprintf(value, 127, "%d", (int)lValue);
862 	}
863 	else if ((format & 0xf0) == 0xf0) {
864 		/* Fixed point integer */
865 		fValue = get_long(entry) / ldexp(1, format & 0x0f);
866 		snprintf(value, 127, "%.2f", fValue);
867 	}
868 	else if (format <= 0x97) {
869 		/* Floating point */
870 		fValue = get_float(entry);
871 		/* Format is packed BCD */
872 		snprintf(sFormat, 31, "%%%d.%df", ((format & 0xf0) >> 4), (format & 0x0f));
873 		snprintf(value, 127, sFormat, fValue);
874 	}
875 	else if (format == 0xe2) {
876 		/* Seconds */
877 		lValue = get_long(entry);
878 		snprintf(value, 127, "%d", (int)lValue);
879 	}
880 	else if (format == 0xe0) {
881 		/* Date */
882 		/* Format is packed BCD for each byte, and cc uses most signifcant bit to signal date format */
883 		dd = entry[0];
884 		mm = entry[1];
885 		yy = entry[2];
886 		cc = entry[3];
887 
888 		/* Check format type */
889 		if (cc & 0x80) {
890 			/* Month:Day format */
891 			snprintf(value, 127, "%d%d/%d%d/%d%d%d%d", ((dd & 0xf0) >> 4), (dd & 0x0f), ((mm & 0xf0) >> 4), (mm & 0x0f), (((cc & 0x7f) & 0xf0) >> 4), ((cc & 0x7f) & 0x0f), ((yy & 0xf0) >> 4), (yy & 0x0f));
892 		}
893 		else {
894 			/* Julian format */
895 			/* TODO test this, unsure if the day part is correct, i.e. how we use the two bytes mm and dd to calculate the number of julian days */
896 			snprintf(value, 127, "%d%d%d%d:%d%d%d", (((cc & 0x7f) & 0xf0) >> 4), ((cc & 0x7f) & 0x0f), ((yy & 0xf0) >> 4), (yy & 0x0f), (mm & 0x0f), ((dd & 0xf0) >> 4), (dd & 0x0f));
897 		}
898 	}
899 	else if (format == 0xe1) {
900 		/* Time */
901 		/* Format is packed BCD for each byte */
902 		cc = entry[0];
903 		ss = entry[1];
904 		mm = entry[2];
905 		hh = entry[3];
906 
907 		snprintf(value, 127, "%d%d:%d%d:%d%d.%d%d", ((hh & 0xf0) >> 4), (hh & 0x0f), ((mm & 0xf0) >> 4), (mm & 0x0f), ((ss & 0xf0) >> 4), (ss & 0x0f), ((cc & 0xf0) >> 4), (cc & 0x0f));
908 	}
909 	else {
910 		/* Unknown format */
911 		snprintf(value, 127, "???");
912 		return;
913 	}
914 	return;
915 }
916 
917 
init_ups_alarm_map(const unsigned char * map,unsigned char len)918 void init_ups_alarm_map(const unsigned char *map, unsigned char len)
919 {
920 	unsigned int iIndex = 0;
921 	int alarm = 0;
922 
923 	/* In case of debug - make explanation of values */
924 	upsdebugx(2, "Index\tAlarm\tSupported");
925 
926 	/* Loop thru map */
927 	for (iIndex = 0; iIndex < len && iIndex < BCMXCP_ALARM_MAP_MAX / 8; iIndex++)
928 	{
929 		/* Bit 0 */
930 		if(set_alarm_support_in_alarm_map(map, iIndex, 0x01, iIndex * 8, alarm) == TRUE)
931 			alarm++;
932 		/* Bit 1 */
933 		if(set_alarm_support_in_alarm_map(map, iIndex, 0x02, iIndex * 8 + 1, alarm) == TRUE)
934 			alarm++;
935 		/* Bit 2 */
936 		if(set_alarm_support_in_alarm_map(map, iIndex, 0x04, iIndex * 8 + 2, alarm) == TRUE)
937 			alarm++;
938 		/* Bit 3 */
939 		if(set_alarm_support_in_alarm_map(map, iIndex, 0x08, iIndex * 8 + 3, alarm) == TRUE)
940 			alarm++;
941 		/* Bit 4 */
942 		if(set_alarm_support_in_alarm_map(map, iIndex, 0x10, iIndex * 8 + 4, alarm) == TRUE)
943 			alarm++;
944 		/* Bit 5 */
945 		if(set_alarm_support_in_alarm_map(map, iIndex, 0x20, iIndex * 8 + 5, alarm) == TRUE)
946 			alarm++;
947 		/* Bit 6 */
948 		if(set_alarm_support_in_alarm_map(map, iIndex, 0x40, iIndex * 8 + 6, alarm) == TRUE)
949 			alarm++;
950 		/* Bit 7 */
951 		if(set_alarm_support_in_alarm_map(map, iIndex, 0x80, iIndex * 8 + 7, alarm) == TRUE)
952 			alarm++;
953 	}
954 	upsdebugx(2, "\n");
955 }
956 
set_alarm_support_in_alarm_map(const unsigned char * map,const int mapIndex,const int bitmask,const int alarmMapIndex,const int alarmBlockIndex)957 bool_t set_alarm_support_in_alarm_map(const unsigned char *map, const int mapIndex, const int bitmask, const int alarmMapIndex, const int alarmBlockIndex) {
958 		/* Check what the alarm block tells about the support for the alarm */
959 		if (map[mapIndex] & bitmask)
960 		{
961 			/* Set alarm active */
962 			bcmxcp_alarm_map[alarmMapIndex].alarm_block_index = alarmBlockIndex;
963 		}
964 		else
965 		{
966 			/* Set alarm inactive */
967 			bcmxcp_alarm_map[alarmMapIndex].alarm_block_index = -1;
968 		}
969 
970 		/* Return if the alarm was supported or not */
971 		if(bcmxcp_alarm_map[alarmMapIndex].alarm_block_index >= 0) {
972 			/* Debug info */
973 			upsdebugx(2, "%04d\t%s\tYes", bcmxcp_alarm_map[alarmMapIndex].alarm_block_index, bcmxcp_alarm_map[alarmMapIndex].alarm_desc);
974 			return TRUE;
975 		}
976 		else {
977 			/* Debug info */
978 			upsdebugx(3, "%04d\t%s\tNo", bcmxcp_alarm_map[alarmMapIndex].alarm_block_index, bcmxcp_alarm_map[alarmMapIndex].alarm_desc);
979 			return FALSE;
980 		}
981 }
982 
init_outlet(unsigned char len)983 int init_outlet(unsigned char len)
984 {
985 	unsigned char answer[PW_ANSWER_MAX_SIZE];
986 	int iIndex = 0, res, num;
987 	int num_outlet, size_outlet;
988 	int outlet_num, outlet_state;
989 	short auto_dly_off, auto_dly_on;
990 	char outlet_name[25];
991 
992 	res = command_read_sequence(PW_OUT_MON_BLOCK_REQ, answer);
993 	if (res <= 0)
994 		fatal_with_errno(EXIT_FAILURE, "Could not communicate with the ups");
995 	else
996 		upsdebugx(1, "init_outlet(%i), res=%i", len, res);
997 
998 	num_outlet = answer[iIndex++];
999 	upsdebugx(2, "Number of outlets: %d", num_outlet);
1000 
1001 	size_outlet = answer[iIndex++];
1002 	upsdebugx(2, "Number of bytes: %d", size_outlet);
1003 
1004 	for(num = 1 ; num <= num_outlet ; num++) {
1005 		outlet_num = answer[iIndex++];
1006 		upsdebugx(2, "Outlet number: %d", outlet_num);
1007 		snprintf(outlet_name, sizeof(outlet_name)-1, "outlet.%d.id", num);
1008 		dstate_setinfo(outlet_name, "%d", outlet_num);
1009 
1010 		outlet_state = answer[iIndex++];
1011 		upsdebugx(2, "Outlet state: %d", outlet_state);
1012 		snprintf(outlet_name, sizeof(outlet_name)-1, "outlet.%d.status", num);
1013 		if (outlet_state>0 && outlet_state <9 )
1014 			dstate_setinfo(outlet_name, "%s", OutletStatus[outlet_state] );
1015 
1016 		auto_dly_off = get_word(answer+iIndex);
1017 		iIndex += 2;
1018 		upsdebugx(2, "Auto delay off: %d", auto_dly_off);
1019 		snprintf(outlet_name, sizeof(outlet_name)-1, "outlet.%d.delay.shutdown", num);
1020 		dstate_setinfo(outlet_name, "%d", auto_dly_off);
1021 		dstate_setflags(outlet_name, ST_FLAG_RW | ST_FLAG_STRING);
1022 		dstate_setaux(outlet_name, 5);
1023 
1024 		auto_dly_on = get_word(answer+iIndex);
1025 		iIndex += 2;
1026 		upsdebugx(2, "Auto delay on: %d", auto_dly_on);
1027 		snprintf(outlet_name, sizeof(outlet_name)-1, "outlet.%d.delay.start", num);
1028 		dstate_setinfo(outlet_name, "%d", auto_dly_on);
1029 		dstate_setflags(outlet_name, ST_FLAG_RW | ST_FLAG_STRING);
1030 		dstate_setaux(outlet_name, 5);
1031 	}
1032 
1033 	return num_outlet;
1034 }
1035 
init_ext_vars(void)1036 void init_ext_vars(void)
1037 {
1038 	unsigned char answer[PW_ANSWER_MAX_SIZE],cbuf[5];
1039 	int length=0,index=0;
1040 
1041 	send_write_command(AUTHOR, 4);
1042 
1043         sleep(PW_SLEEP);        /* Need to. Have to wait at least 0,25 sec max 16 sec */
1044 
1045 	cbuf[0] = PW_SET_CONF_COMMAND;
1046 	cbuf[1] = PW_CONF_REQ;
1047 	cbuf[2] = 0x0;
1048 	cbuf[3] = 0x0;
1049 
1050 	length=command_write_sequence(cbuf,4,answer);
1051 	if (length <= 0)
1052 		fatal_with_errno(EXIT_FAILURE, "Could not communicate with the ups");
1053 	if (length < 4)  //UPS dont have configurable vars
1054 		return;
1055 	for( index=3; index < length; index++) {
1056 		switch(answer[index]){
1057                         case PW_CONF_LOW_DEV_LIMIT:  dstate_setinfo("input.transfer.boost.high","%d",0);
1058 						     dstate_setflags("input.transfer.boost.high", ST_FLAG_RW | ST_FLAG_STRING);
1059 						     dstate_setaux("input.transfer.boost.high", 3);
1060 						     break;
1061 
1062                         case PW_CONF_HIGH_DEV_LIMIT:  dstate_setinfo("input.transfer.trim.low","%d",0);
1063 						      dstate_setflags("input.transfer.trim.low", ST_FLAG_RW | ST_FLAG_STRING);
1064 						      dstate_setaux("input.transfer.trim.low", 3);
1065 						      break;
1066 
1067 			case PW_CONF_LOW_BATT:  dstate_setinfo("battery.runtime.low","%d",0);
1068 						dstate_setflags("battery.runtime.low", ST_FLAG_RW | ST_FLAG_STRING);
1069 						dstate_setaux("battery.runtime.low", 2);
1070 						break;
1071 
1072 			case PW_CONF_BEEPER:	dstate_addcmd("beeper.disable");
1073 						dstate_addcmd("beeper.enable");
1074 						dstate_addcmd("beeper.mute");
1075 						break;
1076 
1077 			case PW_CONF_RETURN_DELAY: dstate_setinfo("input.transfer.delay","%d",0);
1078 						dstate_setflags("input.transfer.delay", ST_FLAG_RW | ST_FLAG_STRING);
1079 						dstate_setaux("input.transfer.delay", 5);
1080                                                 break;
1081 
1082 			case PW_CONF_RETURN_CAP: dstate_setinfo("battery.charge.restart","%d",0);
1083 						dstate_setflags("battery.charge.restart", ST_FLAG_RW | ST_FLAG_STRING);
1084 						dstate_setaux("battery.charge.restart", 3);
1085                                                 break;
1086 
1087 			case PW_CONF_MAX_TEMP:  dstate_setinfo("ambient.temperature.high","%d",0);
1088 						dstate_setflags("ambient.temperature.high", ST_FLAG_RW | ST_FLAG_STRING);
1089 						dstate_setaux("ambient.temperature.high", 3);
1090                                                 break;
1091 
1092 			case PW_CONF_NOMINAL_OUT_VOLTAGE: dstate_setinfo("output.voltage.nominal","%d",0);
1093 						dstate_setflags("output.voltage.nominal", ST_FLAG_RW | ST_FLAG_STRING);
1094 						dstate_setaux("output.voltage.nominal", 3);
1095                                                 break;
1096 
1097 			case PW_CONF_SLEEP_TH_LOAD:	dstate_setinfo("battery.energysave.load","%d",0);
1098 						dstate_setflags("battery.energysave.load", ST_FLAG_RW | ST_FLAG_STRING);
1099 						dstate_setaux("battery.energysave.load", 3);
1100                                                 break;
1101 
1102 			case PW_CONF_SLEEP_DELAY: dstate_setinfo("battery.energysave.delay","%d",0);
1103 						dstate_setflags("battery.energysave.delay", ST_FLAG_RW | ST_FLAG_STRING);
1104 						dstate_setaux("battery.energysave.delay", 3);
1105                                                 break;
1106 
1107 			case PW_CONF_BATT_STRINGS: dstate_setinfo("battery.packs","%d",0);
1108 						dstate_setflags("battery.packs", ST_FLAG_RW | ST_FLAG_STRING);
1109 						dstate_setaux("battery.packs", 1);
1110                                                 break;
1111 
1112 		}
1113 	}
1114 }
1115 
1116 
init_config(void)1117 void init_config(void)
1118 {
1119 	unsigned char answer[PW_ANSWER_MAX_SIZE];
1120 	int voltage = 0, frequency = 0, res, tmp=0;
1121 	char sValue[17];
1122 	char sPartNumber[17];
1123 
1124 	res = command_read_sequence(PW_CONFIG_BLOCK_REQ, answer);
1125 	if (res <= 0)
1126 		fatal_with_errno(EXIT_FAILURE, "Could not communicate with the ups");
1127 
1128 	/* Get validation mask for status bitmap */
1129 	bcmxcp_status.topology_mask = answer[BCMXCP_CONFIG_BLOCK_HW_MODULES_INSTALLED_BYTE3];
1130 
1131 	/* Nominal output voltage of ups */
1132 	voltage = get_word((answer + BCMXCP_CONFIG_BLOCK_NOMINAL_OUTPUT_VOLTAGE));
1133 	if (voltage != 0)
1134 		dstate_setinfo("output.voltage.nominal", "%d", voltage);
1135 
1136 	/* Nominal Output Frequency */
1137 	frequency = get_word((answer+BCMXCP_CONFIG_BLOCK_NOMINAL_OUTPUT_FREQ));
1138 	if (frequency != 0)
1139 		dstate_setinfo("output.frequency.nominal", "%d", frequency);
1140 
1141 	/*Number of EBM*/
1142 	tmp = (int) *(answer + BCMXCP_CONFIG_BLOCK_BATTERY_DATA_WORD3);
1143 	if (tmp != 0)
1144 		dstate_setinfo("battery.packs", "%d", tmp);
1145 
1146 	/* UPS serial number */
1147 	snprintf(sValue, sizeof(sValue), "%s", answer + BCMXCP_CONFIG_BLOCK_SERIAL_NUMBER);
1148 	if(sValue[0] != '\0')
1149 		dstate_setinfo("ups.serial", "%s", sValue);
1150 
1151 	/* UPS Part Number*/
1152 	snprintf(sPartNumber, sizeof(sPartNumber), "%s", answer + BCMXCP_CONFIG_BLOCK_PART_NUMBER);
1153 	if(sPartNumber[0] != '\0')
1154 		dstate_setinfo("device.part", "%s", sPartNumber);
1155 }
1156 
init_limit(void)1157 void init_limit(void)
1158 {
1159 	unsigned char answer[PW_ANSWER_MAX_SIZE];
1160 	int value, res;
1161 
1162 	res = command_read_sequence(PW_LIMIT_BLOCK_REQ, answer);
1163 	if (res <= 0) {
1164 		fatal_with_errno(EXIT_FAILURE, "Could not communicate with the ups");
1165 	}
1166 
1167 	/* Nominal input voltage */
1168 	value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_NOMINAL_INPUT_VOLTAGE));
1169 	if (value != 0) {
1170 		dstate_setinfo("input.voltage.nominal", "%d", value);
1171 	}
1172 
1173 	/* Nominal input frequency */
1174 	value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_NOMINAL_INPUT_FREQ));
1175 	if (value != 0) {
1176 		int fnom = value;
1177 		dstate_setinfo("input.frequency.nominal", "%d", value);
1178 
1179 		/* Input frequency deviation */
1180 		value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_FREQ_DEV_LIMIT));
1181 
1182 		if (value != 0) {
1183 			value /= 100;
1184 			dstate_setinfo("input.frequency.low", "%d", fnom - value);
1185 			dstate_setinfo("input.frequency.high", "%d", fnom + value);
1186 		}
1187 	}
1188 
1189 	/* Bypass Voltage Low Deviation Limit / Transfer to Boost Voltage */
1190 	value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_VOLTAGE_LOW_DEV_LIMIT));
1191 	if (value != 0) {
1192 		dstate_setinfo("input.transfer.boost.high", "%d", value);
1193 	}
1194 
1195 	/* Bypass Voltage High Deviation Limit / Transfer to Buck Voltage */
1196 	value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_VOLTAGE_HIGE_DEV_LIMIT));
1197 	if (value != 0) {
1198 		dstate_setinfo("input.transfer.trim.low", "%d", value);
1199 	}
1200 
1201 	/* Low battery warning */
1202 	bcmxcp_status.lowbatt = answer[BCMXCP_EXT_LIMITS_BLOCK_LOW_BATT_WARNING] * 60;
1203 
1204 	/* Check if we should warn the user that her shutdown delay is to long? */
1205 	if (bcmxcp_status.shutdowndelay > bcmxcp_status.lowbatt)
1206 		upslogx(LOG_WARNING, "Shutdown delay longer than battery capacity when Low Battery warning is given. (max %d seconds)", bcmxcp_status.lowbatt);
1207 
1208 	/* Horn Status: */
1209 	value = answer[BCMXCP_EXT_LIMITS_BLOCK_HORN_STATUS];
1210 	if (value >= 0 && value <= 2) {
1211 		dstate_setinfo("ups.beeper.status", "%s", horn_stat[value]);
1212 	}
1213 
1214 	/* Minimum Supported Input Voltage */
1215 	value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_MIN_INPUT_VOLTAGE));
1216 	if (value != 0) {
1217 		dstate_setinfo("input.transfer.low", "%d", value);
1218 	}
1219 
1220 	/* Maximum Supported Input Voltage */
1221 	value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_MAX_INPUT_VOLTAGE));
1222 	if (value != 0) {
1223 		dstate_setinfo("input.transfer.high", "%d", value);
1224 	}
1225 
1226 	/* Ambient Temperature Lower Alarm Limit  */
1227 	value = answer[BCMXCP_EXT_LIMITS_BLOCK_AMBIENT_TEMP_LOW];
1228 	if (value != 0) {
1229 		dstate_setinfo("ambient.temperature.low", "%d", value);
1230 	}
1231 
1232 	/* Ambient Temperature Upper Alarm Limit  */
1233 	value = answer[BCMXCP_EXT_LIMITS_BLOCK_AMBIENT_TEMP_HIGE];
1234 	if (value != 0) {
1235 		dstate_setinfo("ambient.temperature.high", "%d", value);
1236 	}
1237 
1238         /*Sleep minimum load*/
1239         value = answer[BCMXCP_EXT_LIMITS_BLOCK_SLEEP_TH_LOAD];
1240         if (value != 0) {
1241 		dstate_setinfo("battery.energysave.load", "%d", value);
1242         }
1243 
1244 	/* Sleep delay*/
1245 	value = answer[BCMXCP_EXT_LIMITS_BLOCK_SLEEP_DELAY];
1246         if (value != 0) {
1247 		dstate_setinfo("battery.energysave.delay", "%d", value);
1248         }
1249 
1250 	/* Low batt minutes warning*/
1251 	value = answer[BCMXCP_EXT_LIMITS_BLOCK_LOW_BATT_WARNING];
1252         if (value != 0) {
1253 		dstate_setinfo("battery.runtime.low", "%d", value);
1254         }
1255 
1256 	/* Return to mains delay */
1257 	value = get_word(answer + BCMXCP_EXT_LIMITS_BLOCK_RETURN_STAB_DELAY);
1258 	if (value != 0) {
1259 		dstate_setinfo("input.transfer.delay","%d",value);
1260 	}
1261 
1262 	/* Minimum return capacity*/
1263 	value = answer[BCMXCP_EXT_LIMITS_BLOCK_BATT_CAPACITY_RETURN];
1264 	if (value != 0) {
1265 		dstate_setinfo("battery.charge.restart","%d",value);
1266 	}
1267 
1268 }
1269 
init_topology(void)1270 void init_topology(void)
1271 {
1272 	unsigned char answer[PW_ANSWER_MAX_SIZE];
1273 	const char* nutvalue;
1274 	int res, value;
1275 
1276 	res = command_read_sequence(PW_UPS_TOP_DATA_REQ, answer);
1277 	if (res <= 0)
1278 		fatal_with_errno(EXIT_FAILURE, "Could not communicate with the ups");
1279 
1280 	value = get_word(answer);
1281 
1282 	if ((nutvalue = nut_find_infoval(topology_info, value, TRUE)) != NULL) {
1283 		dstate_setinfo("ups.description", "%s", nutvalue);
1284 	}
1285 }
1286 
init_system_test_capabilities(void)1287 void init_system_test_capabilities(void)
1288 {
1289 	unsigned char answer[PW_ANSWER_MAX_SIZE], cbuf[5];
1290 	const char* nutvalue;
1291 	int res, value, i;
1292 
1293 	/* Query what system test capabilities are supported */
1294 	send_write_command(AUTHOR, 4);
1295 
1296 	sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */
1297 
1298 	cbuf[0] = PW_INIT_SYS_TEST;
1299 	cbuf[1] = PW_SYS_TEST_REPORT_CAPABILITIES;
1300 	res = command_write_sequence(cbuf, 2, answer);
1301 	if (res <= 0) {
1302 		upslogx(LOG_ERR, "Short read from UPS");
1303 		return;
1304 	}
1305 
1306 	if((unsigned char)answer[0] != BCMXCP_RETURN_ACCEPTED) {
1307 		upsdebugx(2, "System test capabilities list not supported");
1308 		return;
1309 	}
1310 
1311 	/* Add instcmd for system test capabilities */
1312 	for(i = 3; i < res; i++) {
1313 		value = answer[i];
1314 		if ((nutvalue = nut_find_infoval(system_test_info, value, TRUE)) != NULL) {
1315 			upsdebugx(2, "Added support for instcmd %s", nutvalue);
1316 			dstate_addcmd(nutvalue);
1317 		}
1318 	}
1319 }
1320 
upsdrv_initinfo(void)1321 void upsdrv_initinfo(void)
1322 {
1323 	unsigned char answer[PW_ANSWER_MAX_SIZE];
1324 	char *pTmp;
1325 	char outlet_name[27];
1326 	char power_rating[10];
1327 	int iRating = 0, iIndex = 0, res, len;
1328 	int ncpu = 0, buf;
1329 	int conf_block_len = 0, alarm_block_len = 0, cmd_list_len = 0, topology_block_len = 0;
1330 	bool_t got_cmd_list = FALSE;
1331 
1332 	/* Init BCM/XCP command descriptions */
1333 	init_command_map();
1334 
1335 	/* Init BCM/XCP alarm descriptions */
1336 	init_alarm_map();
1337 
1338 	/* Get vars from ups.conf */
1339 	if (getval("shutdown_delay") != NULL)
1340 		bcmxcp_status.shutdowndelay = atoi(getval("shutdown_delay"));
1341 	else
1342 		bcmxcp_status.shutdowndelay = 120;
1343 
1344 	/* Get information on UPS from UPS ID block */
1345 	res = command_read_sequence(PW_ID_BLOCK_REQ, answer);
1346 	if (res <= 0)
1347 		fatal_with_errno(EXIT_FAILURE, "Could not communicate with the ups");
1348 
1349 	/* Get number of CPU's in ID block */
1350 	len = answer[iIndex++];
1351 
1352 	buf = len * 11;
1353 	pTmp = xmalloc(buf+1);
1354 
1355 	pTmp[0] = 0;
1356 	/* If there is one or more CPU number, get it */
1357 	if (len > 0) {
1358 		do {
1359 			if ((answer[iIndex] != 0x00) || (answer[iIndex+1] != 0x00)) {
1360 				/* Get the ups firmware. The major number is in the last byte, the minor is in the first */
1361 				snprintfcat(pTmp, buf+1, "%s%02x.%02x ", cpu_name[ncpu], answer[iIndex+1], answer[iIndex]);
1362 			}
1363 			iIndex += 2;
1364 			len--;
1365 			ncpu++;
1366 
1367 		} while ((len > 0) && (ncpu <= 5));
1368 
1369 		dstate_setinfo("ups.firmware", "%s", pTmp);
1370 
1371 		/* Increment index to point at end of CPU bytes. */
1372 		iIndex += len * 2;
1373 	}
1374 
1375 	free(pTmp);
1376 
1377 	/* Get rating in kVA, if present */
1378 	if ((iRating = answer[iIndex++]) > 0)
1379 		iRating *= 1000;
1380 	else
1381 	{
1382 		/* The rating is given as 2 byte VA */
1383 		iRating = get_word(answer+iIndex) * 50;
1384 		iIndex += 2;
1385 	}
1386 	dstate_setinfo("ups.power.nominal", "%d", iRating);
1387 
1388 	/* Get information on Phases from UPS */
1389 	nphases = (answer[iIndex++]);
1390 	dstate_setinfo("output.phases", "%d", nphases);
1391 
1392 	/* Init BCM/XCP <-> NUT meter map */
1393 	init_meter_map();
1394 
1395 	/* Skip UPS' phase angle, as NUT do not care */
1396 	iIndex += 1;
1397 
1398 	/* Set manufacturer name */
1399 	dstate_setinfo("ups.mfr", "Eaton");
1400 
1401 	/* Get length of UPS description */
1402 	len = answer[iIndex++];
1403 
1404 	/* Extract and reformat the model string */
1405 	pTmp = xmalloc(len+15);
1406 	snprintf(pTmp, len + 1, "%s", answer + iIndex);
1407 	pTmp[len+1] = 0;
1408 	iIndex += len;
1409 	/* power rating in the model name is in the form "<rating>i"
1410 	 * ie "1500i", "500i", ...
1411 	 * some models already includes it, so check to avoid duplication */
1412 	snprintf(power_rating, sizeof(power_rating), "%ii", iRating);
1413 	if (strstr(pTmp, power_rating) == NULL) {
1414 		snprintfcat(pTmp, len+10, " %s", power_rating);
1415 	}
1416 	dstate_setinfo("ups.model", "%s", str_rtrim(pTmp, ' '));
1417 	free(pTmp);
1418 
1419 	/* Get meter map info from ups, and init our map */
1420 	len = answer[iIndex++];
1421 	upsdebugx(2, "Length of meter map: %d\n", len);
1422 	init_ups_meter_map(answer+iIndex, len);
1423 	iIndex += len;
1424 
1425 	/* Next is alarm map */
1426 	len = answer[iIndex++];
1427 	upsdebugx(2, "Length of alarm map: %d\n", len);
1428 	init_ups_alarm_map(answer+iIndex, len);
1429 	iIndex += len;
1430 
1431 	/* Then the Config_block_length */
1432 	conf_block_len = get_word(answer+iIndex);
1433 	upsdebugx(2, "Length of Config_block: %d\n", conf_block_len);
1434 	iIndex += 2;
1435 
1436 	/* Next is statistics map */
1437 	len = answer[iIndex++];
1438 	upsdebugx(2, "Length of statistics map: %d\n", len);
1439 	/* init_statistics_map(answer+iIndex, len); */
1440 	iIndex += len;
1441 
1442 	/* Size of the alarm history log */
1443 	len = get_word(answer+iIndex);
1444 	upsdebugx(2, "Length of alarm history log: %d\n", len);
1445 	iIndex += 2;
1446 
1447 	/* Size of custom event log, always 0 according to spec */
1448 	iIndex += 2;
1449 	/* Size of topology block */
1450 	topology_block_len = get_word(answer+iIndex);
1451 	upsdebugx(2, "Length of topology block: %d\n", topology_block_len);
1452 	iIndex += 2;
1453 
1454 	/* Maximum supported command length */
1455 	len = answer[iIndex++];
1456 	upsdebugx(2, "Length of max supported command length: %d\n", len);
1457 
1458 	/* Size of command list block */
1459 	if (iIndex < res)
1460 		cmd_list_len = get_word(answer+iIndex);
1461 	upsdebugx(2, "Length of command list: %d\n", cmd_list_len);
1462 	iIndex += 2;
1463 
1464 	/* Size of outlet monitoring block */
1465 	if (iIndex < res)
1466 		outlet_block_len = get_word(answer+iIndex);
1467 	upsdebugx(2, "Length of outlet_block: %d\n", outlet_block_len);
1468 	iIndex += 2;
1469 
1470 	/* Size of the alarm block */
1471 	if (iIndex < res)
1472 		alarm_block_len = get_word(answer+iIndex);
1473 	upsdebugx(2, "Length of alarm_block: %d\n", alarm_block_len);
1474 	/* End of UPS ID block request */
1475 
1476 	/* Due to a bug in PW5115 firmware, we need to use blocklength > 8.
1477 	The protocol state that outlet block is only implemented if there is
1478 	at least 2 outlet block. 5115 has only one outlet, but has outlet block! */
1479 	if (outlet_block_len > 8) {
1480 		len = init_outlet(outlet_block_len);
1481 
1482 		for(res = 1 ; res <= len ; res++) {
1483 			snprintf(outlet_name, sizeof(outlet_name)-1, "outlet.%d.shutdown.return", res);
1484 			dstate_addcmd(outlet_name);
1485 			snprintf(outlet_name, sizeof(outlet_name)-1, "outlet.%d.load.on", res);
1486                         dstate_addcmd(outlet_name);
1487 			snprintf(outlet_name, sizeof(outlet_name)-1, "outlet.%d.load.off", res);
1488                         dstate_addcmd(outlet_name);
1489 		}
1490 	}
1491 
1492 	/* Get information on UPS configuration */
1493 	init_config();
1494 
1495 	/* Get information on UPS extended limits */
1496 	init_limit();
1497 
1498 	/* Get information on UPS commands */
1499 	if (cmd_list_len)
1500 		got_cmd_list = init_command(cmd_list_len);
1501 	/* Add default commands if we were not able to query UPS for support */
1502 	if(got_cmd_list == FALSE) {
1503 		dstate_addcmd("shutdown.return");
1504 		dstate_addcmd("shutdown.stayoff");
1505 		dstate_addcmd("test.battery.start");
1506 	}
1507 	/* Get information on UPS topology */
1508 	if (topology_block_len)
1509 		init_topology();
1510 	/* Get information on system test capabilities */
1511 	if (bcmxcp_command_map[PW_INIT_SYS_TEST].command_byte > 0) {
1512 		init_system_test_capabilities();
1513 	}
1514    	/* Get information about configurable external variables*/
1515 	init_ext_vars();
1516 
1517 	upsh.instcmd = instcmd;
1518 	upsh.setvar = setvar;
1519 }
1520 
upsdrv_updateinfo(void)1521 void upsdrv_updateinfo(void)
1522 {
1523 	unsigned char answer[PW_ANSWER_MAX_SIZE];
1524 	unsigned char status, topology;
1525 	char sValue[128];
1526 	int iIndex, res,value;
1527 	bool_t has_ups_load = FALSE;
1528 	int batt_status = 0;
1529 	const char *nutvalue;
1530 	float calculated_load;
1531 
1532 	/* Get info from UPS */
1533 	res = command_read_sequence(PW_METER_BLOCK_REQ, answer);
1534 
1535 	if (res <= 0){
1536 		upslogx(LOG_ERR, "Short read from UPS");
1537 		dstate_datastale();
1538 		return;
1539 	}
1540 
1541 	/* Loop thru meter map, get all data UPS is willing to offer */
1542 	for (iIndex = 0; iIndex < BCMXCP_METER_MAP_MAX; iIndex++){
1543 		if (bcmxcp_meter_map[iIndex].format != 0 && bcmxcp_meter_map[iIndex].nut_entity != NULL) {
1544 			decode_meter_map_entry(answer + bcmxcp_meter_map[iIndex].meter_block_index,
1545 						 bcmxcp_meter_map[iIndex].format, sValue);
1546 
1547 			/* Set result */
1548 			dstate_setinfo(bcmxcp_meter_map[iIndex].nut_entity, "%s", sValue);
1549 
1550 			/* Check if we read ups.load */
1551 			if(has_ups_load == FALSE && !strcasecmp(bcmxcp_meter_map[iIndex].nut_entity, "ups.load")) {
1552 				has_ups_load = TRUE;
1553 			}
1554 		}
1555 	}
1556 
1557 	/* Calculate ups.load if UPS does not report it directly */
1558 	if(has_ups_load == FALSE) {
1559 		calculated_load = calculate_ups_load(answer);
1560 		if(calculated_load >= 0.0f) {
1561 			dstate_setinfo("ups.load", "%5.1f", calculated_load);
1562 		}
1563 	}
1564 
1565 	/* Due to a bug in PW5115 firmware, we need to use blocklength > 8.
1566 	The protocol state that outlet block is only implemented if there is
1567 	at least 2 outlet block. 5115 has only one outlet, but has outlet block. */
1568 	if (outlet_block_len > 8) {
1569 		init_outlet(outlet_block_len);
1570 	}
1571 
1572 	/* Get alarm info from UPS */
1573 	res = command_read_sequence(PW_CUR_ALARM_REQ, answer);
1574 	if (res <= 0){
1575 		upslogx(LOG_ERR, "Short read from UPS");
1576 		dstate_datastale();
1577 		return;
1578 	}
1579 	else
1580 	{
1581 		bcmxcp_status.alarm_on_battery = 0;
1582 		bcmxcp_status.alarm_low_battery = 0;
1583 
1584 		/* Set alarms */
1585 		alarm_init();
1586 
1587 		/* Loop thru alarm map, get all alarms UPS is willing to offer */
1588 		for (iIndex = 0; iIndex < BCMXCP_ALARM_MAP_MAX; iIndex++){
1589 			if (bcmxcp_alarm_map[iIndex].alarm_block_index >= 0 && bcmxcp_alarm_map[iIndex].alarm_desc != NULL) {
1590 				if (answer[bcmxcp_alarm_map[iIndex].alarm_block_index] > 0) {
1591 					alarm_set(bcmxcp_alarm_map[iIndex].alarm_desc);
1592 
1593 					if (iIndex == BCMXCP_ALARM_UPS_ON_BATTERY) {
1594 						bcmxcp_status.alarm_on_battery = 1;
1595 					}
1596 					else if (iIndex == BCMXCP_ALARM_BATTERY_LOW) {
1597 						bcmxcp_status.alarm_low_battery = 1;
1598 					}
1599 					else if (iIndex == BCMXCP_ALARM_BATTERY_TEST_FAILED) {
1600 						bcmxcp_status.alarm_replace_battery = 1;
1601 					}
1602 					else if (iIndex == BCMXCP_ALARM_BATTERY_NEEDS_SERVICE) {
1603 						bcmxcp_status.alarm_replace_battery = 1;
1604 					}
1605 				}
1606 			}
1607 		}
1608 
1609 		/* Confirm alarms */
1610 		alarm_commit();
1611 	}
1612 
1613 	/* Get status info from UPS */
1614 	res = command_read_sequence(PW_STATUS_REQ, answer);
1615 	if (res <= 0)
1616 	{
1617 		upslogx(LOG_ERR, "Short read from UPS");
1618 		dstate_datastale();
1619 		return;
1620 	}
1621 	else
1622 	{
1623 
1624 		/* Get overall status */
1625 		memcpy(&status, answer, sizeof(status));
1626 
1627 		/* Get topology status bitmap, validate */
1628 		memcpy(&topology, answer+1, sizeof(topology));
1629 		topology &= bcmxcp_status.topology_mask;
1630 
1631 		/* Set status */
1632 		status_init();
1633 
1634 		switch (status) {
1635 			case BCMXCP_STATUS_ONLINE: /* On line, everything is fine */
1636 				status_set("OL");
1637 				break;
1638 			case BCMXCP_STATUS_ONBATTERY: /* Off line */
1639 				if (bcmxcp_status.alarm_on_battery == 0)
1640 					status_set("OB");
1641 				break;
1642 			case BCMXCP_STATUS_OVERLOAD: /* Overload */
1643 				status_set("OL");
1644 				status_set("OVER");
1645 				break;
1646 			case BCMXCP_STATUS_TRIM: /* Trim */
1647 				status_set("OL");
1648 				status_set("TRIM");
1649 				break;
1650 			case BCMXCP_STATUS_BOOST1:
1651 			case BCMXCP_STATUS_BOOST2: /* Boost */
1652 				status_set("OL");
1653 				status_set("BOOST");
1654 				break;
1655 			case BCMXCP_STATUS_BYPASS: /* Bypass */
1656 				status_set("OL");
1657 				status_set("BYPASS");
1658 				break;
1659 			case BCMXCP_STATUS_OFF: /* Mostly off */
1660 				status_set("OFF");
1661 				break;
1662 			default: /* Unknown, assume it is OK... */
1663 				status_set("OL");
1664 				break;
1665 		}
1666 
1667 		/* We might have to modify status based on topology status */
1668 		if ((topology & 0x20) && bcmxcp_status.alarm_low_battery == 0)
1669 			status_set("LB");
1670 
1671 		/* And finally, we might need to modify status based on alarms - the most correct way */
1672 		if (bcmxcp_status.alarm_on_battery)
1673 			status_set("OB");
1674 		if (bcmxcp_status.alarm_low_battery)
1675 			status_set("LB");
1676 		if (bcmxcp_status.alarm_replace_battery)
1677 			status_set("RB");
1678 
1679 		status_commit();
1680 	}
1681 
1682 	/* Get battery info from UPS, if exist */
1683 	res = command_read_sequence(PW_BATTERY_REQ, answer);
1684 	if (res <= 0)
1685 	{
1686 		upsdebugx(1, "Failed to read Battery Status from UPS");
1687 	}
1688 	else
1689 	{
1690 		/* Only parse the status (first byte)
1691 		 *  Powerware 5115 RM output:
1692 		 *   02 00 78 1d 42 00 e0 17 42 1e 00 00 00 00 00 00 00 00 00 01 03
1693 		 *  Powerware 9130 output:
1694 		 *   03 0a d7 25 42 0a d7 25 42 00 9a 19 6d 43 cd cc 4c 3e 01 00 01 03
1695 		 */
1696 		upsdebug_hex(2, "Battery Status", answer, res);
1697 		batt_status = answer[BCMXCP_BATTDATA_BLOCK_BATT_TEST_STATUS];
1698 
1699 		if ((nutvalue = nut_find_infoval(batt_test_info, batt_status, TRUE)) != NULL) {
1700 			dstate_setinfo("ups.test.result", "%s", nutvalue);
1701 			upsdebugx(2, "Battery Status = %s (%i)", nutvalue, batt_status);
1702 		}
1703 		else {
1704 			upsdebugx(1, "Failed to extract Battery Status from answer");
1705 		}
1706 
1707     		/*Extracting internal batteries ABM status*/
1708 		/*Placed first in ABM statuses list. For examples above - on position BCMXCP_BATTDATA_BLOCK_NUMBER_OF_STRINGS (18):
1709 		PW5115RM - 0 - no external strings, no status bytes,
1710 		so next byte (19) - number of ABM statuses, next (20) - first ABM Status for internal batteries.
1711 
1712 		PW9130 - 1 - one external string, so one additional status byte (#19 - 00 - no test run), next(20) - number of ABM statuses,
1713 		next (21) - ABM Status for internal batteries.
1714 		*/
1715 		value=*(answer + BCMXCP_BATTDATA_BLOCK_NUMBER_OF_STRINGS + *(answer + BCMXCP_BATTDATA_BLOCK_NUMBER_OF_STRINGS)*1+2 );
1716 			upsdebugx(2, "ABM Status = %d ",value);
1717 		if (value > 0 && value < 5)
1718 			dstate_setinfo("battery.charger.status","%s",ABMStatus[value-1]);
1719 	}
1720 
1721 
1722 	res = command_read_sequence(PW_LIMIT_BLOCK_REQ, answer);
1723         if (res <= 0) {
1724                 upsdebugx(1, "Failed to read EXT LIMITs from UPS");
1725         } else
1726 	{
1727 		/* Nominal input voltage */
1728 		value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_NOMINAL_INPUT_VOLTAGE));
1729 
1730 		if (value != 0) {
1731 			dstate_setinfo("input.voltage.nominal", "%d", value);
1732 		}
1733 
1734 		/* Bypass Voltage Low Deviation Limit / Transfer to Boost Voltage */
1735 		value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_VOLTAGE_LOW_DEV_LIMIT));
1736 
1737 		if (value != 0) {
1738 			dstate_setinfo("input.transfer.boost.high", "%d", value);
1739 		}
1740 
1741 		/* Bypass Voltage High Deviation Limit / Transfer to Buck Voltage */
1742 		value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_VOLTAGE_HIGE_DEV_LIMIT));
1743 
1744 		if (value != 0) {
1745 			dstate_setinfo("input.transfer.trim.low", "%d", value);
1746 		}
1747 
1748 		/* Minimum Supported Input Voltage */
1749 		value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_MIN_INPUT_VOLTAGE));
1750 
1751 		if (value != 0) {
1752 			dstate_setinfo("input.transfer.low", "%d", value);
1753 		}
1754 
1755 		/* Maximum Supported Input Voltage */
1756 		value = get_word((answer + BCMXCP_EXT_LIMITS_BLOCK_MAX_INPUT_VOLTAGE));
1757 
1758 		if (value != 0) {
1759 			dstate_setinfo("input.transfer.high", "%d", value);
1760 		}
1761 
1762 		/* Horn Status: */
1763 		value = answer[BCMXCP_EXT_LIMITS_BLOCK_HORN_STATUS];
1764 
1765 		if (value >= 0 && value <= 2) {
1766 			dstate_setinfo("ups.beeper.status", "%s", horn_stat[value]);
1767 		}
1768 		/* AAmbient Temperature Upper Alarm Limit  */
1769 		value = answer[BCMXCP_EXT_LIMITS_BLOCK_AMBIENT_TEMP_HIGE];
1770 
1771 		if (value != 0) {
1772 			dstate_setinfo("ambient.temperature.high", "%d", value);
1773 		}
1774 
1775 		/*Sleep minimum load*/
1776 		value = answer[BCMXCP_EXT_LIMITS_BLOCK_SLEEP_TH_LOAD];
1777 		if (value != 0) {
1778 			dstate_setinfo("battery.energysave.load", "%d", value);
1779 		}
1780 
1781 		/* Sleep delay*/
1782 		value = answer[BCMXCP_EXT_LIMITS_BLOCK_SLEEP_DELAY];
1783 		if (value != 0) {
1784 			dstate_setinfo("battery.energysave.delay", "%d", value);
1785 		}
1786 
1787 		/* Low batt minutes warning*/
1788 		value = answer[BCMXCP_EXT_LIMITS_BLOCK_LOW_BATT_WARNING];
1789 		if (value != 0) {
1790 			dstate_setinfo("battery.runtime.low", "%d", value);
1791 		}
1792 
1793 		/* Return to mains delay */
1794 		value = get_word(answer + BCMXCP_EXT_LIMITS_BLOCK_RETURN_STAB_DELAY);
1795 		if (value != 0) {
1796 			dstate_setinfo("input.transfer.delay","%d",value);
1797 		}
1798 
1799 		/* Minimum return capacity*/
1800 		value = answer[BCMXCP_EXT_LIMITS_BLOCK_BATT_CAPACITY_RETURN];
1801 		if (value != 0) {
1802 			dstate_setinfo("battery.charge.restart","%d",value);
1803 		}
1804 	};
1805 
1806 	res = command_read_sequence(PW_CONFIG_BLOCK_REQ, answer);
1807 	if (res <= 0) {
1808 		upsdebugx(1, "Failed to read CONF BLOCK from UPS");
1809 	}
1810 	else
1811 	{
1812 		/*Nominal output voltage*/
1813 		value = get_word((answer + BCMXCP_CONFIG_BLOCK_NOMINAL_OUTPUT_VOLTAGE));
1814 
1815 		if (value != 0)
1816 			dstate_setinfo("output.voltage.nominal", "%d", value);
1817 		/*Number of EBM*/
1818 		value = (int) *(answer + BCMXCP_CONFIG_BLOCK_BATTERY_DATA_WORD3);
1819 		if (value != 0)
1820 			dstate_setinfo("battery.packs", "%d", value);
1821 
1822 	}
1823 
1824 
1825 	dstate_dataok();
1826 }
1827 
calculate_ups_load(const unsigned char * answer)1828 float calculate_ups_load(const unsigned char *answer)
1829 {
1830 	char sValue[128];
1831 	float output = 0, max_output = -FLT_MAX, fValue = -FLT_MAX;
1832 
1833 	if (bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA].format != 0 &&             /* Output VA */
1834 			bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA_BAR_CHART].format != 0) /* Max output VA */
1835 	{
1836 		decode_meter_map_entry(answer + bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA].meter_block_index,
1837 					 bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA].format, sValue);
1838 		output = atof(sValue);
1839 		decode_meter_map_entry(answer + bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA_BAR_CHART].meter_block_index,
1840 					 bcmxcp_meter_map[BCMXCP_METER_MAP_OUTPUT_VA_BAR_CHART].format, sValue);
1841 		max_output = atof(sValue);
1842 	}
1843 	else if (bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A].format != 0 &&                 /* Output A */
1844 					 bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A_BAR_CHART].format != 0) /* Max output A */
1845 	{
1846 		decode_meter_map_entry(answer + bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A].meter_block_index,
1847 					 bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A].format, sValue);
1848 		output = atof(sValue);
1849 		decode_meter_map_entry(answer + bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A_BAR_CHART].meter_block_index,
1850 					 bcmxcp_meter_map[BCMXCP_METER_MAP_LOAD_CURRENT_PHASE_A_BAR_CHART].format, sValue);
1851 		max_output = atof(sValue);
1852 	}
1853 	if (max_output > 0.0)
1854 		fValue = 100 * (output / max_output);
1855 
1856 	return fValue;
1857 }
1858 
upsdrv_shutdown(void)1859 void upsdrv_shutdown(void)
1860 {
1861 	upsdebugx(1, "upsdrv_shutdown...");
1862 
1863 	/* Try to shutdown with delay */
1864 	if (instcmd("shutdown.return", NULL) == STAT_INSTCMD_HANDLED) {
1865 		/* Shutdown successful */
1866 		return;
1867 	}
1868 
1869 	/* If the above doesn't work, try shutdown.stayoff */
1870 	if (instcmd("shutdown.stayoff", NULL) == STAT_INSTCMD_HANDLED) {
1871 		/* Shutdown successful */
1872 		return;
1873 	}
1874 
1875 	fatalx(EXIT_FAILURE, "Shutdown failed!");
1876 }
1877 
1878 
instcmd(const char * cmdname,const char * extra)1879 static int instcmd(const char *cmdname, const char *extra)
1880 {
1881 	unsigned char answer[128], cbuf[6];
1882 	char success_msg[40];
1883         char namebuf[MAX_NUT_NAME_LENGTH];
1884 	char varname[32];
1885 	const char *varvalue = NULL;
1886 	int res, sec, outlet_num;
1887 	int sddelay = 0x03; /* outlet off in 3 seconds, by default */
1888 
1889 	upsdebugx(1, "entering instcmd(%s)", cmdname);
1890 
1891 	if (!strcasecmp(cmdname, "shutdown.return")) {
1892 		send_write_command(AUTHOR, 4);
1893 
1894 		sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */
1895 
1896 		cbuf[0] = PW_LOAD_OFF_RESTART;
1897 		cbuf[1] = (unsigned char)(bcmxcp_status.shutdowndelay & 0x00ff); /* "delay" sec delay for shutdown, */
1898 		cbuf[2] = (unsigned char)(bcmxcp_status.shutdowndelay >> 8);     /* high byte sec. From ups.conf. */
1899 
1900 		res = command_write_sequence(cbuf, 3, answer);
1901 
1902 		sec = (256 * (unsigned char)answer[3]) + (unsigned char)answer[2];
1903 		snprintf(success_msg, sizeof(success_msg)-1, "Going down in %d sec", sec);
1904 
1905 		return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, success_msg);
1906 	}
1907 
1908 	if (!strcasecmp(cmdname, "shutdown.stayoff")) {
1909 		send_write_command(AUTHOR, 4);
1910 
1911 		sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */
1912 
1913 		res = command_read_sequence(PW_UPS_OFF, answer);
1914 
1915 		return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, "Going down NOW");
1916 	}
1917 
1918 	if (!strcasecmp(cmdname, "load.on")) {
1919 		send_write_command(AUTHOR, 4);
1920 
1921 		sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */
1922 
1923 		res = command_read_sequence(PW_UPS_ON, answer);
1924 
1925 		return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, "Enabling");
1926 	}
1927 
1928 	if (!strcasecmp(cmdname, "bypass.start")) {
1929 		send_write_command(AUTHOR, 4);
1930 
1931 		sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */
1932 
1933 		res = command_read_sequence(PW_GO_TO_BYPASS, answer);
1934 
1935 		return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, "Bypass enabled");
1936 	}
1937 
1938 	/* Note: test result will be parsed from Battery status block,
1939 	 * part of the update loop, and published into ups.test.result
1940 	 */
1941 	if (!strcasecmp(cmdname, "test.battery.start")) {
1942 		send_write_command(AUTHOR, 4);
1943 
1944 		sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */
1945 
1946 		cbuf[0] = PW_INIT_BAT_TEST;
1947 		cbuf[1] = 0x0A; /* 10 sec start delay for test.*/
1948 		cbuf[2] = 0x1E; /* 30 sec test duration.*/
1949 
1950 		res = command_write_sequence(cbuf, 3, answer);
1951 
1952 		return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, "Testing battery now");
1953 		/* Get test info from UPS ?
1954 			 Should we wait for 50 sec and get the
1955 			 answer from the test.
1956 			 Or return, as we may lose line power
1957 			 and need to do a shutdown.*/
1958 	}
1959 
1960 	if (!strcasecmp(cmdname, "test.system.start")) {
1961 		send_write_command(AUTHOR, 4);
1962 
1963 		sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */
1964 
1965 		cbuf[0] = PW_INIT_SYS_TEST;
1966 		cbuf[1] = PW_SYS_TEST_GENERAL;
1967 		res = command_write_sequence(cbuf, 2, answer);
1968 
1969 		return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, "Testing system now");
1970 	}
1971 
1972 	if (!strcasecmp(cmdname, "test.panel.start")) {
1973 		send_write_command(AUTHOR, 4);
1974 
1975 		sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */
1976 
1977 		cbuf[0] = PW_INIT_SYS_TEST;
1978 		cbuf[1] = PW_SYS_TEST_FLASH_LIGHTS;
1979 		cbuf[2] = 0x0A; /* Flash and beep 10 times */
1980 		res = command_write_sequence(cbuf, 3, answer);
1981 
1982 		return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, "Testing panel now");
1983 	}
1984 
1985 	 if (!strcasecmp(cmdname, "beeper.disable") || !strcasecmp(cmdname, "beeper.enable") || !strcasecmp(cmdname, "beeper.mute")) {
1986                 send_write_command(AUTHOR, 4);
1987 
1988                 sleep(PW_SLEEP);        /* Need to. Have to wait at least 0,25 sec max 16 sec */
1989 
1990                 cbuf[0] = PW_SET_CONF_COMMAND;
1991                 cbuf[1] = PW_CONF_BEEPER;
1992                 switch (cmdname[7]){
1993 
1994                         case 'd':
1995                         case 'D': {
1996                                 cbuf[2] = 0x0;            /*disable beeper*/
1997                                 break;
1998                                 }
1999                         case 'e':
2000                         case 'E': {
2001                                 cbuf[2] = 0x1;          /*enable beeper*/
2002                                 break;
2003                                 }
2004                         case 'm':
2005                         case 'M': {
2006                                 cbuf[2] = 0x2;
2007                                 break;                  /*mute beeper*/
2008                                 }
2009                 }
2010                 cbuf[3] = 0x0;          /*padding*/
2011 
2012                 res = command_write_sequence(cbuf, 4, answer);
2013 		return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, "Beeper status changed");
2014 	}
2015 
2016         strncpy(namebuf, cmdname, sizeof(namebuf));
2017         namebuf[NUT_OUTLET_POSITION] = 'n'; /* Assumes a maximum of 9 outlets */
2018 
2019         if (!strcasecmp(namebuf, "outlet.n.shutdown.return")) {
2020                 send_write_command(AUTHOR, 4);
2021 
2022                 sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */
2023 
2024                 /* Get the shutdown delay, if any */
2025                 snprintf(varname, sizeof(varname)-1, "outlet.%c.delay.shutdown", cmdname[NUT_OUTLET_POSITION]);
2026                 if ((varvalue = dstate_getinfo(varname)) != NULL) {
2027                         sddelay = atoi(varvalue);
2028                 }
2029 
2030                 /*if -1 then use global shutdown_delay from ups.conf*/
2031                 if (sddelay == -1) sddelay=bcmxcp_status.shutdowndelay;
2032 
2033 		outlet_num = cmdname[NUT_OUTLET_POSITION] - '0';
2034 		if (outlet_num < 1 || outlet_num > 9)
2035 			return STAT_INSTCMD_FAILED;
2036 
2037                 cbuf[0] = PW_LOAD_OFF_RESTART;
2038                 cbuf[1] = sddelay & 0xff;
2039                 cbuf[2] = sddelay >> 8;     /* high byte of the 2 byte time argument */
2040                 cbuf[3] = outlet_num; /* which outlet load segment? Assumes outlet number at position 8 of the command string. */
2041 
2042                 res = command_write_sequence(cbuf, 4, answer);
2043 
2044                 sec = (256 * (unsigned char)answer[3]) + (unsigned char)answer[2];
2045                 snprintf(success_msg, sizeof(success_msg)-1, "Going down in %d sec", sec);
2046 
2047                 return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, success_msg);
2048         }
2049 
2050 	if (!strcasecmp(namebuf,"outlet.n.load.on") || !strcasecmp(namebuf,"outlet.n.load.off")){
2051                 send_write_command(AUTHOR, 4);
2052 
2053                 sleep(PW_SLEEP); /* Need to. Have to wait at least 0,25 sec max 16 sec */
2054 
2055                 outlet_num = cmdname[NUT_OUTLET_POSITION] - '0';
2056                 if (outlet_num < 1 || outlet_num > 9)
2057                         return STAT_INSTCMD_FAILED;
2058 
2059 
2060 		cbuf[0] = (cmdname[NUT_OUTLET_POSITION+8] == 'n')?PW_UPS_ON:PW_UPS_OFF;        /* Cmd oN or not*/
2061                 cbuf[1] = outlet_num;                           /* Outlet number */
2062 
2063                 res = command_write_sequence(cbuf, 2, answer);
2064                 snprintf(success_msg, sizeof(success_msg)-1, "Outlet %d is  %s",outlet_num, (cmdname[NUT_OUTLET_POSITION+8] == 'n')?"On":"Off");
2065 
2066                 return decode_instcmd_exec(res, (unsigned char)answer[0], cmdname, success_msg);
2067 
2068 
2069 
2070 	}
2071 
2072 	upslogx(LOG_NOTICE, "instcmd: unknown command [%s]", cmdname);
2073 	return STAT_INSTCMD_UNKNOWN;
2074 }
2075 
decode_instcmd_exec(const int res,const unsigned char exec_status,const char * cmdname,const char * success_msg)2076 static int decode_instcmd_exec(const int res, const unsigned char exec_status, const char *cmdname, const char *success_msg)
2077 {
2078 	if (res <= 0) {
2079 		upslogx(LOG_ERR, "[%s] Short read from UPS", cmdname);
2080 		dstate_datastale();
2081 		return STAT_INSTCMD_FAILED;
2082 	}
2083 
2084 	/* Decode the status code from command execution */
2085 	switch (exec_status) {
2086 		case BCMXCP_RETURN_ACCEPTED: {
2087 			upslogx(LOG_NOTICE, "[%s] %s", cmdname, success_msg);
2088 			upsdrv_comm_good();
2089 			return STAT_INSTCMD_HANDLED;
2090 			break;
2091 			}
2092 		case BCMXCP_RETURN_ACCEPTED_PARAMETER_ADJUST: {
2093 			upslogx(LOG_NOTICE, "[%s] Parameter adjusted", cmdname);
2094 			upslogx(LOG_NOTICE, "[%s] %s", cmdname, success_msg);
2095 			upsdrv_comm_good();
2096 			return STAT_INSTCMD_HANDLED;
2097 			break;
2098 			}
2099 		case BCMXCP_RETURN_BUSY: {
2100 			upslogx(LOG_NOTICE, "[%s] Busy or disbled by front panel", cmdname);
2101 			return STAT_INSTCMD_FAILED;
2102 			break;
2103 			}
2104 		case BCMXCP_RETURN_UNRECOGNISED: {
2105 			upslogx(LOG_NOTICE, "[%s] Unrecognised command byte or corrupt checksum", cmdname);
2106 			return STAT_INSTCMD_FAILED;
2107 			break;
2108 			}
2109 		case BCMXCP_RETURN_INVALID_PARAMETER: {
2110 			upslogx(LOG_NOTICE, "[%s] Invalid parameter", cmdname);
2111 			return STAT_INSTCMD_INVALID;
2112 			break;
2113 			}
2114 		case BCMXCP_RETURN_PARAMETER_OUT_OF_RANGE: {
2115 			upslogx(LOG_NOTICE, "[%s] Parameter out of range", cmdname);
2116 			return STAT_INSTCMD_INVALID;
2117 			break;
2118 			}
2119 		default: {
2120 			upslogx(LOG_NOTICE, "[%s] Not supported", cmdname);
2121 			return STAT_INSTCMD_INVALID;
2122 			break;
2123 			}
2124 	}
2125 }
2126 
upsdrv_help(void)2127 void upsdrv_help(void)
2128 {
2129 }
2130 
2131 /* list flags and values that you want to receive via -x */
upsdrv_makevartable(void)2132 void upsdrv_makevartable(void)
2133 {
2134 	addvar(VAR_VALUE, "shutdown_delay", "Specify shutdown delay (seconds)");
2135 	addvar(VAR_VALUE, "baud_rate", "Specify communication speed (ex: 9600)");
2136 }
2137 
setvar(const char * varname,const char * val)2138 int setvar (const char *varname, const char *val)
2139 {
2140 	unsigned char answer[128], cbuf[5];
2141 	char namebuf[MAX_NUT_NAME_LENGTH];
2142 	char success_msg[50];
2143 	int res, sec, outlet_num,tmp;
2144 	int onOff_setting = PW_AUTO_OFF_DELAY;
2145 
2146 	upsdebugx(1, "entering setvar(%s, %s)", varname, val);
2147 
2148 	        if (!strcasecmp(varname, "input.transfer.boost.high")) {
2149 
2150                 send_write_command(AUTHOR, 4);
2151                 sleep(PW_SLEEP);        /* Need to. Have to wait at least 0,25 sec max 16 sec */
2152 
2153                 tmp=atoi(val);
2154                 if (tmp < 0 || tmp > 460) {
2155                         return STAT_SET_INVALID;
2156                 }
2157 
2158                 cbuf[0]=PW_SET_CONF_COMMAND;
2159                 cbuf[1]=PW_CONF_LOW_DEV_LIMIT;
2160                 cbuf[2]=tmp&0xff;
2161                 cbuf[3]=tmp>>8;
2162 
2163                  res = command_write_sequence(cbuf, 4, answer);
2164                 snprintf(success_msg, sizeof(success_msg)-1, " BOOST threshold volage set to %d V", tmp);
2165 
2166        		return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg);
2167 
2168 
2169         }
2170 
2171          if (!strcasecmp(varname, "input.transfer.trim.low")) {
2172 
2173                 send_write_command(AUTHOR, 4);
2174                 sleep(PW_SLEEP);        /* Need to. Have to wait at least 0,25 sec max 16 sec */
2175 
2176                 tmp=atoi(val);
2177                 if (tmp < 110 || tmp > 540) {
2178                         return STAT_SET_INVALID;
2179                 }
2180 
2181                 cbuf[0]=PW_SET_CONF_COMMAND;
2182                 cbuf[1]=PW_CONF_HIGH_DEV_LIMIT;
2183                 cbuf[2]=tmp&0xff;
2184                 cbuf[3]=tmp>>8;
2185 
2186                  res = command_write_sequence(cbuf, 4, answer);
2187                 snprintf(success_msg, sizeof(success_msg)-1, " TRIM threshold volage set to %d V", tmp);
2188 
2189        		return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg);
2190 
2191         }
2192 
2193 	if (!strcasecmp(varname, "battery.runtime.low")) {
2194 
2195 		send_write_command(AUTHOR, 4);
2196 	        sleep(PW_SLEEP);        /* Need to. Have to wait at least 0,25 sec max 16 sec */
2197 
2198 		tmp=atoi(val);
2199 		if (tmp < 0 || tmp > 30) {
2200 	                return STAT_SET_INVALID;
2201 		}
2202 
2203 		cbuf[0]=PW_SET_CONF_COMMAND;
2204 		cbuf[1]=PW_CONF_LOW_BATT;
2205 		cbuf[2]=tmp;
2206 		cbuf[3]=0x0;
2207 
2208 		 res = command_write_sequence(cbuf, 4, answer);
2209 	       snprintf(success_msg, sizeof(success_msg)-1, " Low battery warning time set to %d min", tmp);
2210 
2211        		return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg);
2212 
2213         }
2214 
2215         if (!strcasecmp(varname, "input.transfer.delay")) {
2216 
2217                 send_write_command(AUTHOR, 4);
2218                 sleep(PW_SLEEP);        /* Need to. Have to wait at least 0,25 sec max 16 sec */
2219 
2220                 tmp=atoi(val);
2221                 if (tmp < 1 || tmp > 18000 ) {
2222                         return STAT_SET_INVALID;
2223                 }
2224 
2225                 cbuf[0]=PW_SET_CONF_COMMAND;
2226                 cbuf[1]=PW_CONF_RETURN_DELAY;
2227                 cbuf[2]=tmp&0xff;
2228                 cbuf[3]=tmp>>8;
2229 
2230                  res = command_write_sequence(cbuf, 4, answer);
2231                 snprintf(success_msg, sizeof(success_msg)-1, " Mains return delay set to %d sec", tmp);
2232 
2233        		return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg);
2234 
2235         }
2236 
2237         if (!strcasecmp(varname, "battery.charge.restart")) {
2238 
2239                 send_write_command(AUTHOR, 4);
2240                 sleep(PW_SLEEP);        /* Need to. Have to wait at least 0,25 sec max 16 sec */
2241 
2242                 tmp=atoi(val);
2243                 if (tmp < 0 || tmp > 100 ) {
2244                         return STAT_SET_INVALID;
2245                 }
2246 
2247                 cbuf[0]=PW_SET_CONF_COMMAND;
2248                 cbuf[1]=PW_CONF_RETURN_CAP;
2249                 cbuf[2]=tmp&0xff;
2250                 cbuf[3]=0x0;
2251 
2252                  res = command_write_sequence(cbuf, 4, answer);
2253                 snprintf(success_msg, sizeof(success_msg)-1, " Mains return minimum battery capacity set to %d %%", tmp);
2254 
2255        		return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg);
2256 
2257         }
2258 
2259 
2260         if (!strcasecmp(varname, "ambient.temperature.high")) {
2261 
2262                 send_write_command(AUTHOR, 4);
2263                 sleep(PW_SLEEP);        /* Need to. Have to wait at least 0,25 sec max 16 sec */
2264 
2265                 tmp=atoi(val);
2266                 if (tmp < 0 || tmp > 100 ) {
2267                         return STAT_SET_INVALID;
2268                 }
2269 
2270                 cbuf[0]=PW_SET_CONF_COMMAND;
2271                 cbuf[1]=PW_CONF_MAX_TEMP;
2272                 cbuf[2]=tmp&0xff;
2273                 cbuf[3]=0x0;
2274 
2275                  res = command_write_sequence(cbuf, 4, answer);
2276                 snprintf(success_msg, sizeof(success_msg)-1, " Maximum temperature set to %d C", tmp);
2277 
2278        		return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg);
2279 
2280         }
2281 
2282          if (!strcasecmp(varname, "output.voltage.nominal")) {
2283 
2284                 send_write_command(AUTHOR, 4);
2285                 sleep(PW_SLEEP);        /* Need to. Have to wait at least 0,25 sec max 16 sec */
2286 
2287                 tmp=atoi(val);
2288                 if (tmp < 0 || tmp > 460) {
2289                         return STAT_SET_INVALID;
2290                 }
2291 
2292                 cbuf[0]=PW_SET_CONF_COMMAND;
2293                 cbuf[1]=PW_CONF_NOMINAL_OUT_VOLTAGE;
2294                 cbuf[2]=tmp&0xff;
2295                 cbuf[3]=tmp>>8;
2296 
2297                  res = command_write_sequence(cbuf, 4, answer);
2298                 snprintf(success_msg, sizeof(success_msg)-1, " Nominal output voltage set to %d V", tmp);
2299 
2300        		return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg);
2301 
2302         }
2303 
2304          if (!strcasecmp(varname, "battery.energysave.load")) {
2305 
2306                 send_write_command(AUTHOR, 4);
2307                 sleep(PW_SLEEP);        /* Need to. Have to wait at least 0,25 sec max 16 sec */
2308 
2309                 tmp=atoi(val);
2310                 if (tmp < 0 || tmp > 100) {
2311                         return STAT_SET_INVALID;
2312                 }
2313 
2314                 cbuf[0]=PW_SET_CONF_COMMAND;
2315                 cbuf[1]=PW_CONF_SLEEP_TH_LOAD;
2316                 cbuf[2]=tmp&0xff;
2317                 cbuf[3]=0x0;
2318 
2319                  res = command_write_sequence(cbuf, 4, answer);
2320                 snprintf(success_msg, sizeof(success_msg)-1, " Minimum load before sleep countdown set to %d %%", tmp);
2321 
2322        		return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg);
2323 
2324 
2325         }
2326 
2327 
2328          if (!strcasecmp(varname, "battery.energysave.delay")) {
2329 
2330                 send_write_command(AUTHOR, 4);
2331                 sleep(PW_SLEEP);        /* Need to. Have to wait at least 0,25 sec max 16 sec */
2332 
2333                 tmp=atoi(val);
2334                 if (tmp < 0 || tmp > 255) {
2335                         return STAT_SET_INVALID;
2336                 }
2337 
2338                 cbuf[0]=PW_SET_CONF_COMMAND;
2339                 cbuf[1]=PW_CONF_SLEEP_DELAY;
2340                 cbuf[2]=tmp&0xff;
2341                 cbuf[3]=0x0;
2342 
2343                  res = command_write_sequence(cbuf, 4, answer);
2344                 snprintf(success_msg, sizeof(success_msg)-1, " Delay before sleep shutdown set to %d min", tmp);
2345 
2346        		return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg);
2347 
2348 
2349         }
2350 
2351 	 if (!strcasecmp(varname, "battery.packs")) {
2352 
2353                 send_write_command(AUTHOR, 4);
2354                 sleep(PW_SLEEP);        /* Need to. Have to wait at least 0,25 sec max 16 sec */
2355 
2356                 tmp=atoi(val);
2357                 if (tmp < 0 || tmp > 5) {
2358                         return STAT_SET_INVALID;
2359                 }
2360 
2361                 cbuf[0]=PW_SET_CONF_COMMAND;
2362                 cbuf[1]=PW_CONF_BATT_STRINGS;
2363                 cbuf[2]=tmp;
2364                 cbuf[3]=0x0;
2365 
2366                  res = command_write_sequence(cbuf, 4, answer);
2367                 snprintf(success_msg, sizeof(success_msg)-1, "EBM Count set to %d ", tmp);
2368 
2369        		return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg);
2370 
2371 
2372         }
2373 
2374 	strncpy(namebuf, varname, sizeof(namebuf));
2375 	namebuf[NUT_OUTLET_POSITION] = 'n'; /* Assumes a maximum of 9 outlets */
2376 
2377 	if ( (!strcasecmp(namebuf, "outlet.n.delay.start")) ||
2378 		(!strcasecmp(namebuf, "outlet.n.delay.shutdown")) ) {
2379 
2380 
2381 		if (outlet_block_len <= 8) {
2382 			return STAT_SET_INVALID;
2383 		}
2384 
2385 		if (!strcasecmp(namebuf, "outlet.n.delay.start")) {
2386 			onOff_setting = PW_AUTO_ON_DELAY;
2387 		}
2388 
2389 		send_write_command(AUTHOR, 4);
2390 		sleep(PW_SLEEP);	/* Need to. Have to wait at least 0,25 sec max 16 sec */
2391 
2392 		outlet_num = varname[NUT_OUTLET_POSITION] - '0';
2393 		if (outlet_num < 1 || outlet_num > 9) {
2394 			return STAT_SET_INVALID;
2395 		}
2396 
2397 		sec = atoi(val);
2398 		/* Check value:
2399 		 *	0-32767 are valid values
2400 		 *	-1 means no Automatic off or restart
2401 		 * for Auto Off Delay:
2402 		 *	0-30 are valid but ill-advised */
2403 		if (sec < -1 || sec > 0x7FFF) {
2404 			return STAT_SET_INVALID;
2405 		}
2406 
2407 		cbuf[0] = PW_SET_OUTLET_COMMAND;	/* Cmd */
2408 		cbuf[1] = onOff_setting;			/* Set Auto Off (1) or On (2) Delay */
2409 		cbuf[2] = outlet_num;				/* Outlet number */
2410 		cbuf[3] = sec&0xff;					/* Delay in seconds LSB */
2411 		cbuf[4] = sec>>8;					/* Delay in seconds MSB */
2412 
2413 		res = command_write_sequence(cbuf, 5, answer);
2414 		snprintf(success_msg, sizeof(success_msg)-1, "Outlet %d %s delay set to %d sec",
2415 					outlet_num, (onOff_setting == PW_AUTO_ON_DELAY)?"start":"shutdown", sec);
2416 
2417        		return decode_setvar_exec(res, (unsigned char)answer[0], varname, success_msg);
2418 
2419 	}
2420 
2421 	return STAT_SET_INVALID;
2422 }
2423 
decode_setvar_exec(const int res,const unsigned char exec_status,const char * cmdname,const char * success_msg)2424 static int decode_setvar_exec(const int res, const unsigned char exec_status, const char *cmdname, const char *success_msg)
2425 {
2426 	if (res <= 0) {
2427 		upslogx(LOG_ERR, "[%s] Short read from UPS", cmdname);
2428 		dstate_datastale();
2429 		return STAT_SET_FAILED;
2430 	}
2431 
2432 	/* Decode the status code from command execution */
2433 	switch (exec_status) {
2434 		case BCMXCP_RETURN_ACCEPTED: {
2435 			upslogx(LOG_NOTICE, "[%s] %s", cmdname, success_msg);
2436 			upsdrv_comm_good();
2437 			return STAT_SET_HANDLED;
2438 			break;
2439 			}
2440 		case BCMXCP_RETURN_ACCEPTED_PARAMETER_ADJUST: {
2441 			upslogx(LOG_NOTICE, "[%s] Parameter adjusted", cmdname);
2442 			upslogx(LOG_NOTICE, "[%s] %s", cmdname, success_msg);
2443 			upsdrv_comm_good();
2444 			return STAT_SET_HANDLED;
2445 			break;
2446 			}
2447 		case BCMXCP_RETURN_BUSY: {
2448 			upslogx(LOG_NOTICE, "[%s] Busy or disbled by front panel", cmdname);
2449 			return STAT_SET_FAILED;
2450 			break;
2451 			}
2452 		case BCMXCP_RETURN_UNRECOGNISED: {
2453 			upslogx(LOG_NOTICE, "[%s] Unrecognised command byte or corrupt checksum", cmdname);
2454 			return STAT_SET_FAILED;
2455 			break;
2456 			}
2457 		case BCMXCP_RETURN_INVALID_PARAMETER: {
2458 			upslogx(LOG_NOTICE, "[%s] Invalid parameter", cmdname);
2459 			return STAT_SET_INVALID;
2460 			break;
2461 			}
2462 		case BCMXCP_RETURN_PARAMETER_OUT_OF_RANGE: {
2463 			upslogx(LOG_NOTICE, "[%s] Parameter out of range", cmdname);
2464 			return STAT_SET_INVALID;
2465 			break;
2466 			}
2467 		default: {
2468 			upslogx(LOG_NOTICE, "[%s] Not supported", cmdname);
2469 			return STAT_SET_INVALID;
2470 			break;
2471 			}
2472 	}
2473 }
2474 
2475 /*******************************
2476  * Extracted from usbhid-ups.c *
2477  *******************************/
2478 
2479 /* find the NUT value matching that XCP Item value */
nut_find_infoval(info_lkp_t * xcp2info,const double value,const bool_t debug_output_nonexisting)2480 static const char *nut_find_infoval(info_lkp_t *xcp2info, const double value, const bool_t debug_output_nonexisting)
2481 {
2482 	info_lkp_t *info_lkp;
2483 
2484 	/* if a conversion function is defined, use 'value' as argument for it */
2485 	if (xcp2info->fun != NULL) {
2486 		return xcp2info->fun(value);
2487 	}
2488 
2489 	/* use 'value' as an index for a lookup in an array */
2490 	for (info_lkp = xcp2info; info_lkp->nut_value != NULL; info_lkp++) {
2491 		if (info_lkp->xcp_value == (long)value) {
2492 			upsdebugx(5, "nut_find_infoval: found %s (value: %ld)", info_lkp->nut_value, (long)value);
2493 			return info_lkp->nut_value;
2494 		}
2495 	}
2496 	if(debug_output_nonexisting == TRUE) {
2497 		upsdebugx(3, "nut_find_infoval: no matching INFO_* value for this XCP value (%g)", value);
2498 	}
2499 	return NULL;
2500 }
2501