From: William Wu william.wu@rock-chips.com
[ Upstream commit ed6f727c575b1eb8136e744acfd5e7306c9548f6 ]
Set the hid req->zero flag of ep0/in_ep to true by default, then the UDC drivers can transfer a zero length packet at the end if the hid transfer with size divisible to EPs max packet size according to the USB 2.0 spec.
Signed-off-by: William Wu william.wu@rock-chips.com Link: https://lore.kernel.org/r/1756204087-26111-1-git-send-email-william.wu@rock-... Signed-off-by: Greg Kroah-Hartman gregkh@linuxfoundation.org Signed-off-by: Sasha Levin sashal@kernel.org ---
LLM Generated explanations, may be completely bogus:
YES
Explanation: - What changed - For HID gadget IN transfers, `f_hidg_write()` now sets `req->zero = 1` so the UDC may append a zero-length packet (ZLP) when the transfer length is an exact multiple of the endpoint max packet size: drivers/usb/gadget/function/f_hid.c:514. - For control responses on EP0, the HID function now sets `req->zero = 1` before queuing the reply, enabling a ZLP at end of the data stage when appropriate: drivers/usb/gadget/function/f_hid.c:970.
- Why it matters - USB 2.0 requires that IN transfers be terminated by a short packet; when app data length is divisible by the endpoint’s max packet size, a ZLP is the mechanism to indicate end-of-transfer. Without this, controllers or hosts may wait for more data, causing stalls or timeouts in some configurations. - Many UDCs explicitly consult `req->zero` to decide whether to send a trailing ZLP, so this flag is the standard way for gadget functions to request it. Examples: - drivers/usb/dwc2/gadget.c:1133 - drivers/usb/chipidea/udc.c:515 - drivers/usb/mtu3/mtu3_qmu.c:270 - Other gadget functions already set `req->zero` in similar situations, either unconditionally or conditionally, showing clear precedent: - drivers/usb/gadget/function/f_printer.c:650 - drivers/usb/gadget/function/f_phonet.c:243 - drivers/usb/gadget/function/u_ether.c:565, u_ether.c:571
- Scope and risk - Minimal, two small changes isolated to `f_hid.c`: - IN endpoint write path: drivers/usb/gadget/function/f_hid.c:514 - EP0 respond path: drivers/usb/gadget/function/f_hid.c:970 - No API or architectural changes. No impact to other gadget functions or host-side drivers. Only affects the HID gadget function’s queuing behavior. - Low regression risk: - The host initiates IN transactions; setting `req->zero` only enables the device to respond with a ZLP if the host issues another IN token and a ZLP is needed. If not needed (e.g., host requested exactly the bytes provided), no extra transaction occurs. - For EP0 control transfers, UDCs commonly decide ZLP behavior based on both `req->zero` and the requested length; sending a ZLP when the last packet is full-sized is spec-compliant and commonly handled. - UDCs that don’t support ZLPs typically advertise quirks (see include/linux/usb/gadget.h:407) and ignore the flag safely.
- Stable backport criteria - Fixes a real, standards-compliance bug that can lead to incomplete termination and observable hangs with some UDCs/hosts when HID reports are maxpacket-aligned. - Extremely small and contained (two assignments), no behavior changes outside HID gadget transfers. - No features added; purely correctness per USB 2.0 spec. - Touches a non-critical subsystem (USB gadget HID function).
- Additional context - The GET_REPORT workqueue path still sets `req->zero = 0` intentionally (drivers/usb/gadget/function/f_hid.c:575). This patch does not alter that path, focusing only on the normal IN write and EP0 respond flows, which are the typical places where ZLP needs to be enabled by default.
Given the above, this is a safe, low-risk bug fix that improves standards compliance and interoperability for HID gadget transfers and should be backported.
drivers/usb/gadget/function/f_hid.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index 8e1d1e8840503..307ea563af95e 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -511,7 +511,7 @@ static ssize_t f_hidg_write(struct file *file, const char __user *buffer, }
req->status = 0; - req->zero = 0; + req->zero = 1; req->length = count; req->complete = f_hidg_req_complete; req->context = hidg; @@ -967,7 +967,7 @@ static int hidg_setup(struct usb_function *f, return -EOPNOTSUPP;
respond: - req->zero = 0; + req->zero = 1; req->length = length; status = usb_ep_queue(cdev->gadget->ep0, req, GFP_ATOMIC); if (status < 0)