git: fbedcad985b5 - stable/13 - Implement an API for sending a zero-length-packet. The purpose of such a USB packet is to toggle the binary packet counter for USB 1.0/2.0 protocols, without sending any data, so that the first packet sent after opening a USB BULK endpoint doesn't get lost. This is for devices not supporting the USB standard defined clear-stall handling.

From: Hans Petter Selasky <hselasky_at_FreeBSD.org>
Date: Sun, 30 Apr 2023 06:58:09 UTC
The branch stable/13 has been updated by hselasky:

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

commit fbedcad985b52481991636a26409d6f55660a3f0
Author:     Hans Petter Selasky <hselasky@FreeBSD.org>
AuthorDate: 2021-07-06 10:29:57 +0000
Commit:     Hans Petter Selasky <hselasky@FreeBSD.org>
CommitDate: 2023-04-30 06:56:18 +0000

    Implement an API for sending a zero-length-packet. The purpose of such a
    USB packet is to toggle the binary packet counter for USB 1.0/2.0 protocols,
    without sending any data, so that the first packet sent after opening
    a USB BULK endpoint doesn't get lost. This is for devices not supporting
    the USB standard defined clear-stall handling.
    
    Tested by:      jmg
    Sponsored by:   NVIDIA Networking
    
    (cherry picked from commit f83f5d58394db57576bbed6dc7531997cabeb102)
    (cherry picked from commit ec97e9ca1fa543a4a803e84706564d41cd492065)
---
 sys/dev/usb/usb_transfer.c | 59 +++++++++++++++++++++++++++++++++++++++++++---
 sys/dev/usb/usbdi.h        |  3 +++
 2 files changed, 59 insertions(+), 3 deletions(-)

diff --git a/sys/dev/usb/usb_transfer.c b/sys/dev/usb/usb_transfer.c
index 455b23e2c306..20ed2c897aac 100644
--- a/sys/dev/usb/usb_transfer.c
+++ b/sys/dev/usb/usb_transfer.c
@@ -2,7 +2,7 @@
 /*-
  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
  *
- * Copyright (c) 2008 Hans Petter Selasky. All rights reserved.
+ * Copyright (c) 2008-2021 Hans Petter Selasky. All rights reserved.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
@@ -2689,6 +2689,61 @@ usbd_transfer_start_cb(void *arg)
 	}
 }
 
+/*------------------------------------------------------------------------*
+ *	usbd_xfer_set_zlp
+ *
+ * This function sets the USB transfers ZLP flag.
+ *------------------------------------------------------------------------*/
+void
+usbd_xfer_set_zlp(struct usb_xfer *xfer)
+{
+	if (xfer == NULL) {
+		/* tearing down */
+		return;
+	}
+	USB_XFER_LOCK_ASSERT(xfer, MA_OWNED);
+
+	/* avoid any races by locking the USB mutex */
+	USB_BUS_LOCK(xfer->xroot->bus);
+	xfer->flags.send_zlp = 1;
+	USB_BUS_UNLOCK(xfer->xroot->bus);
+}
+
+/*------------------------------------------------------------------------*
+ *	usbd_xfer_get_and_clr_zlp
+ *
+ * This function gets and clears the USB transfers ZLP flag and
+ * queues a zero-length USB transfer if the flag was set.
+ *------------------------------------------------------------------------*/
+uint8_t
+usbd_xfer_get_and_clr_zlp(struct usb_xfer *xfer)
+{
+	uint8_t retval;
+
+	if (xfer == NULL) {
+		/* tearing down */
+		return (0);
+	}
+	USB_XFER_LOCK_ASSERT(xfer, MA_OWNED);
+
+	retval = xfer->flags.send_zlp;
+
+	if (retval != 0) {
+		DPRINTFN(1, "Sending zero-length packet.\n");
+
+		/* avoid any races by locking the USB mutex */
+		USB_BUS_LOCK(xfer->xroot->bus);
+		xfer->flags.send_zlp = 0;
+		USB_BUS_UNLOCK(xfer->xroot->bus);
+
+		/* queue up a zero-length packet */
+		usbd_xfer_set_frame_len(xfer, 0, 0);
+		usbd_xfer_set_frames(xfer, 1);
+		usbd_transfer_submit(xfer);
+	}
+	return (retval);
+}
+
 /*------------------------------------------------------------------------*
  *	usbd_xfer_set_stall
  *
@@ -2733,9 +2788,7 @@ usbd_transfer_clear_stall(struct usb_xfer *xfer)
 
 	/* avoid any races by locking the USB mutex */
 	USB_BUS_LOCK(xfer->xroot->bus);
-
 	xfer->flags.stall_pipe = 0;
-
 	USB_BUS_UNLOCK(xfer->xroot->bus);
 }
 
diff --git a/sys/dev/usb/usbdi.h b/sys/dev/usb/usbdi.h
index 1b3b4af5f717..287e40d59364 100644
--- a/sys/dev/usb/usbdi.h
+++ b/sys/dev/usb/usbdi.h
@@ -218,6 +218,7 @@ struct usb_xfer_flags {
 					 * option only has effect for
 					 * ISOCHRONOUS transfers.
 					 */
+	uint8_t send_zlp:1;		/* send a zero length packet first */
 };
 
 /*
@@ -655,6 +656,8 @@ void	usbd_xfer_set_frame_len(struct usb_xfer *xfer, usb_frcount_t frindex,
 	    usb_frlength_t len);
 void	usbd_xfer_set_timeout(struct usb_xfer *xfer, int timeout);
 void	usbd_xfer_set_frames(struct usb_xfer *xfer, usb_frcount_t n);
+void	usbd_xfer_set_zlp(struct usb_xfer *xfer);
+uint8_t	usbd_xfer_get_and_clr_zlp(struct usb_xfer *xfer);
 void	usbd_xfer_set_stall(struct usb_xfer *xfer);
 int	usbd_xfer_is_stalled(struct usb_xfer *xfer);
 void	usbd_xfer_set_flag(struct usb_xfer *xfer, int flag);