Skip to content

Commit cdee828

Browse files
committed
usbhid-ups: improve handling of transient LIBUSB_ERROR_IO failures
Some devices (e.g., CyberPower CP1500PFCLCD) have firmware bugs that cause random I/O errors on specific HID reports during normal polling. Rather than triggering expensive reconnection attempts that can fail in daemon mode, skip the failing report and continue with remaining polls. Add safety check to detect true device disconnection: if all polls fail during an update cycle (items_succeeded == 0), trigger reconnect as before. This improves stability especially in daemon mode while still detecting real disconnections via other error codes or complete poll failure. Fixes #3116
1 parent f502062 commit cdee828

File tree

2 files changed

+27
-4
lines changed

2 files changed

+27
-4
lines changed

NEWS.adoc

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,12 @@ https://github.com/networkupstools/nut/milestone/12
200200
if too few data points were seen. [#3082, #3095]
201201
* `openups-hid` had `nobattery` definition inverted, causing alarms -- now
202202
fixed to use the same mapping helper as other subdrivers. [issue #3246]
203+
* Improved handling of transient `LIBUSB_ERROR_IO` failures during polling.
204+
Some devices (CyberPower, etc.) have firmware bugs causing random I/O
205+
errors on certain HID reports. The driver now skips failing reports and
206+
continues polling rather than triggering expensive reconnection attempts.
207+
True disconnections are still detected via other error codes or when all
208+
polls fail. [issue #3116]
203209

204210
- `upsd` data server updates:
205211
* Sometimes "Data for UPS [X] is stale" and "UPS [X] data is no longer

drivers/usbhid-ups.c

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2183,6 +2183,7 @@ static bool_t hid_ups_walk(walkmode_t mode)
21832183
hid_info_t *item;
21842184
double value;
21852185
int retcode;
2186+
int items_succeeded = 0; /* Track successful polls to detect total failure */
21862187

21872188
#if !((defined SHUT_MODE) && SHUT_MODE)
21882189
/* extract the VendorId for further testing */
@@ -2373,13 +2374,19 @@ static bool_t hid_ups_walk(walkmode_t mode)
23732374
return FALSE;
23742375

23752376
case LIBUSB_ERROR_IO: /* I/O error */
2376-
/* Uh oh, got to reconnect, with a special suggestion! */
2377-
dstate_setinfo("driver.state", "reconnect.trying");
2377+
/* Some devices have firmware bugs causing transient I/O errors
2378+
* on specific HID reports. Rather than triggering expensive
2379+
* reconnects, skip the failing report and continue. If device
2380+
* is truly disconnected, other error codes will catch it, or
2381+
* all polls fail and safety check below triggers reconnect. */
2382+
upsdebugx(3, "Got LIBUSB_ERROR_IO on item '%s' ReportID=0x%02x - skipping",
2383+
item->info_type ? item->info_type : "(null)",
2384+
item->hiddata ? item->hiddata->ReportID : 0xFF);
23782385
interrupt_pipe_EIO_count++;
2379-
hd = NULL;
2380-
return FALSE;
2386+
continue;
23812387

23822388
case 1:
2389+
items_succeeded++; /* Count successful data retrieval */
23832390
break; /* Found! */
23842391

23852392
case 0:
@@ -2449,6 +2456,16 @@ static bool_t hid_ups_walk(walkmode_t mode)
24492456
}
24502457
}
24512458

2459+
/* Safety check: if we got zero successful polls during update,
2460+
* device may be truly disconnected (not just transient errors).
2461+
* Skip this check during INIT mode where failures are expected. */
2462+
if (items_succeeded == 0 && (mode == HU_WALKMODE_QUICK_UPDATE || mode == HU_WALKMODE_FULL_UPDATE)) {
2463+
upsdebugx(1, "Got zero successful data polls - device may be disconnected");
2464+
dstate_setinfo("driver.state", "reconnect.trying");
2465+
hd = NULL;
2466+
return FALSE;
2467+
}
2468+
24522469
return TRUE;
24532470
}
24542471

0 commit comments

Comments
 (0)