Here’s a revised patch. It treats a zero-length reply on a report-ID
device as an error (-1) rather than returning 0, matching how 1.114
now handles the same case in uhidev_get_report_async_cb(), so the
sync and async paths agree:
Index: sys/dev/usb/uhidev.c
--- sys/dev/usb/uhidev.c 17 Oct 2026 17:57:25 -0000 1.114
+++ sys/dev/usb/uhidev.c
@@ -867,6 +867,10 @@ uhidev_get_report(struct uhidev_softc *s
/* Skip the reportID. */
if (id > 0) {
+ if (actlen == 0)
+ actlen = -1;
+ else if (actlen > 0)
+ actlen--;
memcpy(data, buf + 1, len - 1);
free(buf, M_TEMP, len);
}
On Mon, Oct 12, 2026 at 07:39:00AM -0700, James J. Lippard wrote:
> I finally got around to running a UPS using its data connection to
> shut down systems when a power failure occurs using sensorsd, and
> while trying to build some scripts to manage the peculiarities of
> my setup, I found that I could not read directly from the UPS, though
> I could write to it.
>
> Root cause is that devices with report IDs are getting wrong return
> values on the sync path (async works fine), in uhi_get_report,
> lines 841-872 of uhidev.c. It's returning -1 with errno EIO,
> even on success, while USB_SET_REPORT works correctly.
>
> uhidev_get_report prepends room for the report ID byte (len++),
> does the transfer, and copies len-1 bytes past the ID back to
> the caller, but then returns actlen without subtracting the report
> ID byte.
>
> uhidev_set_report() returns the caller's original len and
> uhidev_get_report_async_cb() already decrements len for id > 0,
> so those paths are already working properly.
>
> My testing found all uhid children failed on read, while upd0 sensors
> work. To show the issue, attach an USB HID device with report IDs and
> a feature report (in my case it's an APC BackUPS 1500), and for any
> one of its uhidN:
>
> struct usb_ctl_report r;
> memset(&r, 0, sizeof(r));
> r.ucr_report = UHID_FEATURE_REPORT;
> if (ioctl(fd, USB_GET_REPORT, &r) == -1)
> err(1, "USB_GET_REPORT"); /* always EIO */
>
>
> Patch: Subtract the report ID byte from actlen, as the async callback
> does:
>
> Index: sys/dev/usb/uhidev.c
> --- sys/dev/usb/uhidev.c
> +++ sys/dev/usb/uhidev.c
> @@ -864,6 +864,8 @@ uhidev_get_report(struct uhidev_softc *sc, int type, int
> id, void *data,
>
> /* Skip the reportID. */
> if (id > 0) {
> + if (actlen > 0)
> + actlen--;
> memcpy(data, buf + 1, len - 1);
> free(buf, M_TEMP, len);
> }
>
>
> --
> Jim Lippard [email protected] https://discord.org/
> GPG Key ID: 0x99FD5CD6
>
>
--
Jim Lippard [email protected] https://discord.org/
GPG Key ID: 0x99FD5CD6