2626#include "powercom-hid.h"
2727#include "usb-common.h"
2828
29- #define POWERCOM_HID_VERSION "PowerCOM HID 0.71 "
29+ #define POWERCOM_HID_VERSION "PowerCOM HID 0.72 "
3030/* FIXME: experimental flag to be put in upsdrv_info */
3131
3232/* PowerCOM */
@@ -62,10 +62,30 @@ static char powercom_scratch_buf[32];
6262 */
6363static char powercom_sdcmd_byte_order_fallback = 0 ;
6464
65+ /* Some devices (Raptor and Smart KING Pro series) follow the protocol
66+ * where we can not set arbitrary value of shutdown delay in seconds
67+ * (like in other powercom UPSes), but the shutdown delay can be set
68+ * only from table "Table of possible delays for Shutdown commands"
69+ * specified at page 17 of the protocol document:
70+ * https://networkupstools.org/protocols/powercom/Software_USB_communication_controller_SKP_series.doc
71+ *
72+ * "Table of possible delays for Shutdown commands" (mentioned for
73+ * `DelayBeforeShutdown` and `DelayBeforeStartup` HID Usages):
74+ * Index 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
75+ * Value 12s 18s 24s 30s 36s 42s 48s 54s 1m 2m 3m 4m 5m 6m 7m 8m 9m 10m
76+ * Sent to UPS .2 .3 .4 .5 .6 .7 .8 .9 01 02 03 04 05 06 07 08 09 10
77+ *
78+ */
79+ static char powercom_sdcmd_discrete_delay = 0 ;
80+
6581static const char * powercom_startup_fun (double value )
6682{
6783 uint16_t i = value ;
6884
85+ /* For powercom_sdcmd_discrete_delay we also read minutes
86+ * from DelayBeforeStartup (same as for older dialects).
87+ * FIXME: ...but theoretically they can be 32-bit (1..99999)
88+ */
6989 snprintf (powercom_scratch_buf , sizeof (powercom_scratch_buf ), "%d" , 60 * (((i & 0x00FF ) << 8 ) + (i >> 8 )));
7090 upsdebugx (3 , "%s: value = %.0f, buf = %s" , __func__ , value , powercom_scratch_buf );
7191
@@ -75,20 +95,40 @@ static const char *powercom_startup_fun(double value)
7595static double powercom_startup_nuf (const char * value )
7696{
7797 const char * s = dstate_getinfo ("ups.delay.start" );
78- uint16_t val , command ;
98+ uint32_t val , command ;
7999 int iv ;
80100
81- iv = atoi (value ? value : s ) / 60 ;
82- if (iv < 0 || (intmax_t )iv > (intmax_t )UINT16_MAX ) {
83- upsdebugx (0 , "%s: value = %d is not in uint16_t range" , __func__ , iv );
101+ /* Start with seconds "as is" - convert into whole minutes */
102+ iv = atoi (value ? value : s ) / 60 ; /* minutes */
103+ #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP ) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS ) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE ) )
104+ # pragma GCC diagnostic push
105+ #endif
106+ #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS
107+ # pragma GCC diagnostic ignored "-Wtype-limits"
108+ #endif
109+ #ifdef HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE
110+ # pragma GCC diagnostic ignored "-Wtautological-constant-out-of-range-compare"
111+ #endif
112+ if (iv < 0 || (intmax_t )iv > (intmax_t )UINT32_MAX ) {
113+ #if (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_PUSH_POP ) && ( (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TYPE_LIMITS ) || (defined HAVE_PRAGMA_GCC_DIAGNOSTIC_IGNORED_TAUTOLOGICAL_CONSTANT_OUT_OF_RANGE_COMPARE ) )
114+ # pragma GCC diagnostic pop
115+ #endif
116+ upsdebugx (0 , "%s: value = %d is not in uint32_t range" , __func__ , iv );
84117 return 0 ;
85118 }
86119
87- /* COMMENTME: What are we doing here, a byte-swap in the word? */
88- val = (uint16_t )iv ;
89- command = (uint16_t )(val << 8 );
90- command += (uint16_t )(val >> 8 );
91- upsdebugx (3 , "%s: value = %s, command = %04X" , __func__ , value , command );
120+ if (powercom_sdcmd_discrete_delay ) {
121+ /* Per spec, DelayBeforeStartup reads and writes
122+ * 4-byte "mmmm" values in minutes (1..99999) */
123+ command = (uint32_t )iv ;
124+ } else {
125+ /* COMMENTME: What are we doing here, a byte-swap in the word? */
126+ val = (uint16_t )iv ;
127+ command = (uint16_t )(val << 8 );
128+ command += (uint16_t )(val >> 8 );
129+ }
130+
131+ upsdebugx (3 , "%s: value = %s, command = 0x%08X" , __func__ , value , command );
92132
93133 return command ;
94134}
@@ -101,6 +141,12 @@ static const char *powercom_shutdown_fun(double value)
101141{
102142 uint16_t i = value ;
103143
144+ /* NOTE: for powercom_sdcmd_discrete_delay mode it seems we
145+ * do not read DelayBeforeShutdown at all (not for time),
146+ * the value is stored and retrieved as DelayBeforeStartup.
147+ * FIXME: Should anything be changed here?
148+ */
149+
104150 if (powercom_sdcmd_byte_order_fallback ) {
105151 /* Legacy behavior */
106152 snprintf (powercom_scratch_buf , sizeof (powercom_scratch_buf ), "%d" , 60 * (i & 0x00FF ) + (i >> 8 ));
@@ -120,25 +166,46 @@ static double powercom_shutdown_nuf(const char *value)
120166 uint16_t val , command ;
121167 int iv ;
122168
123- iv = atoi (value ? value : s );
169+ iv = atoi (value ? value : s ); /* seconds */
124170 if (iv < 0 || (intmax_t )iv > (intmax_t )UINT16_MAX ) {
125171 upsdebugx (0 , "%s: value = %d is not in uint16_t range" , __func__ , iv );
126172 return 0 ;
127173 }
128174
129- val = (uint16_t )iv ;
130- val = val ? val : 1 ; /* 0 sets the maximum delay */
131- if (powercom_sdcmd_byte_order_fallback ) {
132- /* Legacy behavior */
133- command = ((uint16_t )((val % 60 ) << 8 )) + (uint16_t )(val / 60 );
134- command |= 0x4000 ; /* AC RESTART NORMAL ENABLE */
175+ if (powercom_sdcmd_discrete_delay ) {
176+ if (iv <= 12 ) command = 1 ;
177+ else if (iv <= 18 ) command = 2 ;
178+ else if (iv <= 24 ) command = 3 ;
179+ else if (iv <= 30 ) command = 4 ;
180+ else if (iv <= 36 ) command = 5 ;
181+ else if (iv <= 42 ) command = 6 ;
182+ else if (iv <= 48 ) command = 7 ;
183+ else if (iv <= 54 ) command = 8 ;
184+ else if (iv <= 60 ) command = 9 ;
185+ else if (iv <= 120 ) command = 10 ;
186+ else if (iv <= 180 ) command = 11 ;
187+ else if (iv <= 240 ) command = 12 ;
188+ else if (iv <= 300 ) command = 13 ;
189+ else if (iv <= 360 ) command = 14 ;
190+ else if (iv <= 420 ) command = 15 ;
191+ else if (iv <= 480 ) command = 16 ;
192+ else if (iv <= 540 ) command = 17 ;
193+ else command = 18 ;
135194 } else {
136- /* New default */
137- command = ((uint16_t )((val / 60 ) << 8 )) + (uint16_t )(val % 60 );
138- command |= 0x0040 ; /* AC RESTART NORMAL ENABLE */
195+ val = (uint16_t )iv ;
196+ val = val ? val : 1 ; /* 0 sets the maximum delay */
197+ if (powercom_sdcmd_byte_order_fallback ) {
198+ /* Legacy behavior */
199+ command = ((uint16_t )((val % 60 ) << 8 )) + (uint16_t )(val / 60 );
200+ command |= 0x4000 ; /* AC RESTART NORMAL ENABLE */
201+ } else {
202+ /* New default */
203+ command = ((uint16_t )((val / 60 ) << 8 )) + (uint16_t )(val % 60 );
204+ command |= 0x0040 ; /* AC RESTART NORMAL ENABLE */
205+ }
139206 }
140207
141- upsdebugx (3 , "%s: value = %s, command = %04X" , __func__ , value , command );
208+ upsdebugx (3 , "%s: value = %s, command = 0x %04X" , __func__ , value , command );
142209
143210 return command ;
144211}
@@ -153,6 +220,7 @@ static double powercom_stayoff_nuf(const char *value)
153220 uint16_t val , command ;
154221 int iv ;
155222
223+ /* FIXME: Anything for powercom_sdcmd_discrete_delay? */
156224 iv = atoi (value ? value : s );
157225 if (iv < 0 || (intmax_t )iv > (intmax_t )UINT16_MAX ) {
158226 upsdebugx (0 , "%s: value = %d is not in uint16_t range" , __func__ , iv );
@@ -171,7 +239,7 @@ static double powercom_stayoff_nuf(const char *value)
171239 command |= 0x0080 ; /* AC RESTART NORMAL DISABLE */
172240 }
173241
174- upsdebugx (3 , "%s: value = %s, command = %04X" , __func__ , value , command );
242+ upsdebugx (3 , "%s: value = %s, command = 0x %04X" , __func__ , value , command );
175243
176244 return command ;
177245}
@@ -592,6 +660,7 @@ static int powercom_claim(HIDDevice_t *hd)
592660
593661accept :
594662 powercom_sdcmd_byte_order_fallback = testvar ("powercom_sdcmd_byte_order_fallback" );
663+ powercom_sdcmd_discrete_delay = testvar ("powercom_sdcmd_discrete_delay" );
595664
596665 return 1 ;
597666}
0 commit comments