git: 7699ec1558b7 - main - hid: Add HQ_NO_READAHEAD quirk and implement it in usbhid(4)

From: Vladimir Kondratyev <wulf_at_FreeBSD.org>
Date: Sun, 17 Aug 2025 21:02:26 UTC
The branch main has been updated by wulf:

URL: https://cgit.FreeBSD.org/src/commit/?id=7699ec1558b7ec2224a4db992ab63aaa849b26fd

commit 7699ec1558b7ec2224a4db992ab63aaa849b26fd
Author:     Vladimir Kondratyev <wulf@FreeBSD.org>
AuthorDate: 2025-08-17 21:00:44 +0000
Commit:     Vladimir Kondratyev <wulf@FreeBSD.org>
CommitDate: 2025-08-17 21:00:44 +0000

    hid: Add HQ_NO_READAHEAD quirk and implement it in usbhid(4)
    
    It disables interrupt emulation on poll-driven buses like USB and
    forces aquiring of only one USB frame per read(2) operation.
    This avoids an FIDO/U2F issue where IN endpoint data received from
    the device right before the file handle is closed, gets lost.
    
    PR:             263995
    Reviewed by:    aokblast
    Differential revision:  https://reviews.freebsd.org/D51605
---
 sys/dev/hid/hidquirk.h     | 1 +
 sys/dev/usb/input/usbhid.c | 4 +++-
 2 files changed, 4 insertions(+), 1 deletion(-)

diff --git a/sys/dev/hid/hidquirk.h b/sys/dev/hid/hidquirk.h
index 4f8b8acbe201..f6fa9f88c6c9 100644
--- a/sys/dev/hid/hidquirk.h
+++ b/sys/dev/hid/hidquirk.h
@@ -50,6 +50,7 @@
 	HQ(IS_XBOX360GP), 	/* device is XBox 360 GamePad */	\
 	HQ(NOWRITE),		/* device does not support writes */	\
 	HQ(IICHID_SAMPLING),	/* IIC backend runs in sampling mode */	\
+	HQ(NO_READAHEAD),	/* Disable interrupt after one report */\
 									\
 	/* Various quirks */						\
 	HQ(HID_IGNORE),		/* device should be ignored by hid class */ \
diff --git a/sys/dev/usb/input/usbhid.c b/sys/dev/usb/input/usbhid.c
index df810012b3f8..255e639621fe 100644
--- a/sys/dev/usb/input/usbhid.c
+++ b/sys/dev/usb/input/usbhid.c
@@ -114,6 +114,7 @@ struct usbhid_xfer_ctx {
 	void *cb_ctx;
 	int waiters;
 	bool influx;
+	bool no_readahead;
 };
 
 struct usbhid_softc {
@@ -272,7 +273,7 @@ usbhid_intr_handler_cb(struct usbhid_xfer_ctx *xfer_ctx)
 	sc->sc_intr_handler(sc->sc_intr_ctx, xfer_ctx->buf,
 	    xfer_ctx->req.intr.actlen);
 
-	return (0);
+	return (xfer_ctx->no_readahead ? ECANCELED : 0);
 }
 
 static int
@@ -430,6 +431,7 @@ usbhid_intr_start(device_t dev, device_t child __unused)
 		.cb = usbhid_intr_handler_cb,
 		.cb_ctx = sc,
 		.buf = sc->sc_intr_buf,
+		.no_readahead = hid_test_quirk(&sc->sc_hw, HQ_NO_READAHEAD),
 	};
 	sc->sc_xfer_ctx[POLL_XFER(USBHID_INTR_IN_DT)] = (struct usbhid_xfer_ctx) {
 		.req.intr.maxlen =