Skip to content

Commit 828f694

Browse files
committed
drivers/nutdrv_qx.c, nutdrv_qx_voltronic.c, NEWS.adoc, docs/nut.dict: qx_process_answer(): set errno=ETIMEDOUT (not EINVAL) if raised by caller or we had a zero-length read; use this in voltronic_claim() [#3276, #3283]
Signed-off-by: Jim Klimov <jimklimov+nut@gmail.com>
1 parent e4470c2 commit 828f694

File tree

4 files changed

+44
-19
lines changed

4 files changed

+44
-19
lines changed

NEWS.adoc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,12 @@ https://github.com/networkupstools/nut/milestone/12
143143
well -- namely, that we successfully use reasonably many of the existing
144144
mappings. Suggest how user can help improve the driver if too few data
145145
points were seen. [PR #3095]
146+
* The `qx_process_answer()` method was extended to raise `errno=ETIMEDOUT`
147+
(rather than `EINVAL`) for short reads that are zero length (and/or that
148+
`errno` value was set in caller's context already). This was used to make
149+
`Voltronic` protocol initial connections more reliable when the device
150+
controller lags upon first contact (is booting itself?) [issue #3276,
151+
PR #3283]
146152
* New `nutdrv_qx_innovatae` adds support for Ippon INNOVA TAE series which
147153
are similar to Q1 except in reporting of nominal `voltage`, `current`,
148154
`frequency` data (that along with `battery_voltage_reports_one_pack` and

docs/nut.dict

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
personal_ws-1.1 en 3634 utf-8
1+
personal_ws-1.1 en 3636 utf-8
22
AAC
33
AAS
44
ABI
@@ -330,6 +330,7 @@ EE
330330
EEPROM
331331
EFI
332332
EG
333+
EINVAL
333334
EL
334335
ELCD
335336
EMI
@@ -352,6 +353,7 @@ ESS
352353
ESV
353354
ESXi
354355
ETIME
356+
ETIMEDOUT
355357
EUROCASE
356358
EVeRr
357359
EXtreme

drivers/nutdrv_qx.c

Lines changed: 17 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4432,6 +4432,7 @@ static bool_t qx_ups_walk(walkmode_t mode)
44324432
previous_item.answer);
44334433

44344434
/* Process the answer */
4435+
errno = 0;
44354436
retcode = qx_process_answer(item, strlen(item->answer));
44364437

44374438
/* ..otherwise: execute command to get answer from the UPS */
@@ -4663,11 +4664,15 @@ item_t *find_nut_info(const char *varname, const unsigned long flag, const unsig
46634664
/* Process the answer we got back from the UPS
46644665
* Return -1 on errors, 0 on success
46654666
* Can set errno, note that EINVAL means unsupported
4666-
* parameter value here!
4667+
* parameter value here, and ETIMEDOUT can be passed
4668+
* from previous context for short reads, or set
4669+
* unilaterally for zero-length reads!
46674670
*/
46684671
static int qx_process_answer(item_t *item, const size_t len)
46694672
{
4670-
errno = 0;
4673+
/* Initial errno inherited from caller, e.g. may be qx_command()
4674+
* in qx_process(), but may be from other memset() etc. after it
4675+
*/
46714676

46724677
/* Query rejected by the UPS */
46734678
if (subdriver->rejected && !strcasecmp(item->answer, subdriver->rejected)) {
@@ -4679,12 +4684,19 @@ static int qx_process_answer(item_t *item, const size_t len)
46794684

46804685
/* Short reply */
46814686
if (item->answer_len && len < item->answer_len) {
4682-
upsdebugx(2, "%s: short reply (%s) %" PRIuSIZE "<%" PRIuSIZE,
4687+
upsdebug_with_errno(2, "%s: short reply (%s) %" PRIuSIZE "<%" PRIuSIZE,
46834688
__func__, item->info_type, len, item->answer_len);
4684-
errno = EINVAL;
4689+
if (len == 0 || errno == ETIMEDOUT) {
4690+
errno = ETIMEDOUT;
4691+
} else {
4692+
errno = EINVAL;
4693+
}
46854694
return -1;
46864695
}
46874696

4697+
/* Not a systemic error by default */
4698+
errno = 0;
4699+
46884700
/* Wrong leading character */
46894701
if (item->leading && item->answer[0] != item->leading) {
46904702
upsdebugx(2,
@@ -4711,6 +4723,7 @@ static int qx_process_answer(item_t *item, const size_t len)
47114723
snprintf(item->value, sizeof(item->value), "%s", "");
47124724
}
47134725

4726+
/* Reset the common error level, if some method above raised it */
47144727
errno = 0;
47154728
return 0;
47164729
}

drivers/nutdrv_qx_voltronic.c

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1692,13 +1692,26 @@ static int voltronic_claim(void)
16921692
item_t *item = find_nut_info("input.voltage", 0, 0);
16931693

16941694
/* Don't know what happened - should have looked up in the mapping table here! */
1695-
if (!item)
1695+
if (!item) {
1696+
upsdebug_with_errno(4, "%s: did not find 'input.voltage' in mapping table", __func__);
16961697
return 0;
1698+
}
16971699

16981700
/* No reply/Unable to get value */
16991701
if ((query_result = qx_process(item, NULL))) {
17001702
upsdebug_with_errno(4, "%s: failed (%d) to get 'input.voltage'", __func__, query_result);
1701-
return 0;
1703+
1704+
if (errno == ETIMEDOUT) {
1705+
upsdebugx(2, "%s: Sometimes the device is laggy, and we could have posted many queries and the buffer is full of replies to them (or it is still producing the answers); try to sleep, flush it and ask again", __func__);
1706+
usleep(5000000); /* arbitrary 5s delay for the device to maybe produce answers to earlier voltage requests */
1707+
upsdebugx(2, "%s: Retry the query now, buffers will be flushed then", __func__);
1708+
query_result = qx_process(item, NULL);
1709+
}
1710+
1711+
if (query_result) {
1712+
/* Not a known timeout/zero-read initially, or still a bad response */
1713+
return 0;
1714+
}
17021715
}
17031716

17041717
/* Unable to process value */
@@ -1712,25 +1725,16 @@ static int voltronic_claim(void)
17121725

17131726
/* Don't know what happened */
17141727
if (!item) {
1728+
upsdebug_with_errno(4, "%s: did not find 'ups.firmware.aux' in mapping table", __func__);
17151729
dstate_delinfo("input.voltage");
17161730
return 0;
17171731
}
17181732

17191733
/* No reply/Unable to get value */
17201734
if ((query_result = qx_process(item, NULL))) {
17211735
upsdebug_with_errno(4, "%s: failed (%d) to get 'ups.firmware.aux'", __func__, query_result);
1722-
1723-
/*if (errno == EINVAL) {*/
1724-
upsdebugx(2, "%s: Sometimes the device is laggy, and we could have posted many queries and the buffer is full of replies to them; try to flush it and ask again", __func__);
1725-
usleep(5000000); /* arbitrary 5s delay for the device to maybe produce answers to earlier voltage requests */
1726-
upsdebugx(2, "%s: Buffers flushed, retry the query", __func__);
1727-
query_result = qx_process(item, NULL);
1728-
/*}*/
1729-
1730-
if (query_result) {
1731-
dstate_delinfo("input.voltage");
1732-
return 0;
1733-
}
1736+
dstate_delinfo("input.voltage");
1737+
return 0;
17341738
}
17351739

17361740
/* Unable to process value/Protocol out of range */

0 commit comments

Comments
 (0)