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


Reply via email to