Send a zero-length-packet first when opening a BULK endpoint for USB serial

port devices. If it gets eaten it is fine. Many USB device side implementations
don't properly support the clear endpoint halt command and if they do, data is lost
because the transmit FIFO is typically reset when this command is received.

Tested by:	jmg
MFC after:	1 week
Sponsored by:	NVIDIA Networking
This commit is contained in:
Hans Petter Selasky 2021-07-06 12:29:57 +02:00
parent b644d64e8a
commit f83f5d5839
18 changed files with 155 additions and 74 deletions

View file

@ -978,8 +978,7 @@ u3g_attach(device_t dev)
/* set stall by default */
mtx_lock(&sc->sc_mtx);
usbd_xfer_set_stall(sc->sc_xfer[nports][U3G_BULK_WR]);
usbd_xfer_set_stall(sc->sc_xfer[nports][U3G_BULK_RD]);
usbd_xfer_set_zlp(sc->sc_xfer[nports][U3G_BULK_WR]);
mtx_unlock(&sc->sc_mtx);
nports++; /* found one port */
@ -1100,6 +1099,9 @@ u3g_write_callback(struct usb_xfer *xfer, usb_error_t error)
case USB_ST_TRANSFERRED:
case USB_ST_SETUP:
tr_setup:
if (usbd_xfer_get_and_clr_zlp(xfer))
break;
for (frame = 0; frame != U3G_TXFRAMES; frame++) {
usbd_xfer_set_frame_offset(xfer, frame * U3G_TXSIZE, frame);

View file

@ -219,8 +219,7 @@ uark_attach(device_t dev)
}
/* clear stall at first run */
mtx_lock(&sc->sc_mtx);
usbd_xfer_set_stall(sc->sc_xfer[UARK_BULK_DT_WR]);
usbd_xfer_set_stall(sc->sc_xfer[UARK_BULK_DT_RD]);
usbd_xfer_set_zlp(sc->sc_xfer[UARK_BULK_DT_WR]);
mtx_unlock(&sc->sc_mtx);
error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
@ -281,13 +280,16 @@ uark_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
case USB_ST_SETUP:
case USB_ST_TRANSFERRED:
tr_setup:
if (usbd_xfer_get_and_clr_zlp(xfer))
break;
pc = usbd_xfer_get_frame(xfer, 0);
if (ucom_get_data(&sc->sc_ucom, pc, 0,
UARK_BUF_SIZE, &actlen)) {
usbd_xfer_set_frame_len(xfer, 0, actlen);
usbd_transfer_submit(xfer);
}
return;
break;
default: /* Error */
if (error != USB_ERR_CANCELLED) {
@ -295,7 +297,7 @@ uark_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
usbd_xfer_set_stall(xfer);
goto tr_setup;
}
return;
break;
}
}

View file

@ -320,8 +320,7 @@ ubsa_attach(device_t dev)
}
/* clear stall at first run */
mtx_lock(&sc->sc_mtx);
usbd_xfer_set_stall(sc->sc_xfer[UBSA_BULK_DT_WR]);
usbd_xfer_set_stall(sc->sc_xfer[UBSA_BULK_DT_RD]);
usbd_xfer_set_zlp(sc->sc_xfer[UBSA_BULK_DT_WR]);
mtx_unlock(&sc->sc_mtx);
error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
@ -583,13 +582,16 @@ ubsa_write_callback(struct usb_xfer *xfer, usb_error_t error)
case USB_ST_SETUP:
case USB_ST_TRANSFERRED:
tr_setup:
if (usbd_xfer_get_and_clr_zlp(xfer))
break;
pc = usbd_xfer_get_frame(xfer, 0);
if (ucom_get_data(&sc->sc_ucom, pc, 0,
UBSA_BSIZE, &actlen)) {
usbd_xfer_set_frame_len(xfer, 0, actlen);
usbd_transfer_submit(xfer);
}
return;
break;
default: /* Error */
if (error != USB_ERR_CANCELLED) {
@ -597,7 +599,7 @@ ubsa_write_callback(struct usb_xfer *xfer, usb_error_t error)
usbd_xfer_set_stall(xfer);
goto tr_setup;
}
return;
break;
}
}

View file

@ -293,8 +293,7 @@ ubser_attach(device_t dev)
ucom_set_pnpinfo_usb(&sc->sc_super_ucom, dev);
mtx_lock(&sc->sc_mtx);
usbd_xfer_set_stall(sc->sc_xfer[UBSER_BULK_DT_WR]);
usbd_xfer_set_stall(sc->sc_xfer[UBSER_BULK_DT_RD]);
usbd_xfer_set_zlp(sc->sc_xfer[UBSER_BULK_DT_WR]);
usbd_transfer_start(sc->sc_xfer[UBSER_BULK_DT_RD]);
mtx_unlock(&sc->sc_mtx);
@ -410,6 +409,9 @@ ubser_write_callback(struct usb_xfer *xfer, usb_error_t error)
case USB_ST_SETUP:
case USB_ST_TRANSFERRED:
tr_setup:
if (usbd_xfer_get_and_clr_zlp(xfer))
break;
pc = usbd_xfer_get_frame(xfer, 0);
do {
if (ucom_get_data(sc->sc_ucom + sc->sc_curr_tx_unit,
@ -430,7 +432,7 @@ ubser_write_callback(struct usb_xfer *xfer, usb_error_t error)
} while (sc->sc_curr_tx_unit != first_unit);
return;
break;
default: /* Error */
if (error != USB_ERR_CANCELLED) {
@ -438,7 +440,7 @@ ubser_write_callback(struct usb_xfer *xfer, usb_error_t error)
usbd_xfer_set_stall(xfer);
goto tr_setup;
}
return;
break;
}
}

View file

@ -350,8 +350,7 @@ uchcom_attach(device_t dev)
/* clear stall at first run */
mtx_lock(&sc->sc_mtx);
usbd_xfer_set_stall(sc->sc_xfer[UCHCOM_BULK_DT_WR]);
usbd_xfer_set_stall(sc->sc_xfer[UCHCOM_BULK_DT_RD]);
usbd_xfer_set_zlp(sc->sc_xfer[UCHCOM_BULK_DT_WR]);
mtx_unlock(&sc->sc_mtx);
error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
@ -828,6 +827,9 @@ uchcom_write_callback(struct usb_xfer *xfer, usb_error_t error)
case USB_ST_SETUP:
case USB_ST_TRANSFERRED:
tr_setup:
if (usbd_xfer_get_and_clr_zlp(xfer))
break;
pc = usbd_xfer_get_frame(xfer, 0);
if (ucom_get_data(&sc->sc_ucom, pc, 0,
usbd_xfer_max_len(xfer), &actlen)) {

View file

@ -438,8 +438,7 @@ ufoma_attach(device_t dev)
/* clear stall at first run, if any */
mtx_lock(&sc->sc_mtx);
usbd_xfer_set_stall(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_WRITE]);
usbd_xfer_set_stall(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_READ]);
usbd_xfer_set_zlp(sc->sc_bulk_xfer[UFOMA_BULK_ENDPT_WRITE]);
mtx_unlock(&sc->sc_mtx);
error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
@ -791,13 +790,16 @@ ufoma_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
case USB_ST_SETUP:
case USB_ST_TRANSFERRED:
tr_setup:
if (usbd_xfer_get_and_clr_zlp(xfer))
break;
pc = usbd_xfer_get_frame(xfer, 0);
if (ucom_get_data(&sc->sc_ucom, pc, 0,
UFOMA_BULK_BUF_SIZE, &actlen)) {
usbd_xfer_set_frame_len(xfer, 0, actlen);
usbd_transfer_submit(xfer);
}
return;
break;
default: /* Error */
if (error != USB_ERR_CANCELLED) {
@ -805,7 +807,7 @@ ufoma_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
usbd_xfer_set_stall(xfer);
goto tr_setup;
}
return;
break;
}
}

View file

@ -1117,8 +1117,7 @@ uftdi_attach(device_t dev)
}
/* clear stall at first run */
mtx_lock(&sc->sc_mtx);
usbd_xfer_set_stall(sc->sc_xfer[UFTDI_BULK_DT_WR]);
usbd_xfer_set_stall(sc->sc_xfer[UFTDI_BULK_DT_RD]);
usbd_xfer_set_zlp(sc->sc_xfer[UFTDI_BULK_DT_WR]);
mtx_unlock(&sc->sc_mtx);
/* set a valid "lcr" value */
@ -1221,6 +1220,9 @@ uftdi_write_callback(struct usb_xfer *xfer, usb_error_t error)
/* FALLTHROUGH */
case USB_ST_SETUP:
case USB_ST_TRANSFERRED:
if (usbd_xfer_get_and_clr_zlp(xfer))
break;
/*
* If output packets don't require headers (the common case) we
* can just load the buffer up with payload bytes all at once.

View file

@ -233,10 +233,8 @@ ugensa_attach(device_t dev)
break;
}
/* clear stall at first run */
mtx_lock(&sc->sc_mtx);
usbd_xfer_set_stall(ssc->sc_xfer[UGENSA_BULK_DT_WR]);
usbd_xfer_set_stall(ssc->sc_xfer[UGENSA_BULK_DT_RD]);
usbd_xfer_set_zlp(ssc->sc_xfer[UGENSA_BULK_DT_WR]);
mtx_unlock(&sc->sc_mtx);
/* initialize port number */
@ -313,13 +311,16 @@ ugensa_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
case USB_ST_SETUP:
case USB_ST_TRANSFERRED:
tr_setup:
if (usbd_xfer_get_and_clr_zlp(xfer))
break;
pc = usbd_xfer_get_frame(xfer, 0);
if (ucom_get_data(ssc->sc_ucom_ptr, pc, 0,
UGENSA_BUF_SIZE, &actlen)) {
usbd_xfer_set_frame_len(xfer, 0, actlen);
usbd_transfer_submit(xfer);
}
return;
break;
default: /* Error */
if (error != USB_ERR_CANCELLED) {
@ -327,7 +328,7 @@ ugensa_bulk_write_callback(struct usb_xfer *xfer, usb_error_t error)
usbd_xfer_set_stall(xfer);
goto tr_setup;
}
return;
break;
}
}

View file

@ -1157,8 +1157,7 @@ uipaq_attach(device_t dev)
}
/* clear stall at first run */
mtx_lock(&sc->sc_mtx);
usbd_xfer_set_stall(sc->sc_xfer[UIPAQ_BULK_DT_WR]);
usbd_xfer_set_stall(sc->sc_xfer[UIPAQ_BULK_DT_RD]);
usbd_xfer_set_zlp(sc->sc_xfer[UIPAQ_BULK_DT_WR]);
mtx_unlock(&sc->sc_mtx);
error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
@ -1320,13 +1319,16 @@ uipaq_write_callback(struct usb_xfer *xfer, usb_error_t error)
case USB_ST_SETUP:
case USB_ST_TRANSFERRED:
tr_setup:
if (usbd_xfer_get_and_clr_zlp(xfer))
break;
pc = usbd_xfer_get_frame(xfer, 0);
if (ucom_get_data(&sc->sc_ucom, pc, 0,
UIPAQ_BUF_SIZE, &actlen)) {
usbd_xfer_set_frame_len(xfer, 0, actlen);
usbd_transfer_submit(xfer);
}
return;
break;
default: /* Error */
if (error != USB_ERR_CANCELLED) {
@ -1334,7 +1336,7 @@ uipaq_write_callback(struct usb_xfer *xfer, usb_error_t error)
usbd_xfer_set_stall(xfer);
goto tr_setup;
}
return;
break;
}
}

View file

@ -218,6 +218,9 @@ ulpt_write_callback(struct usb_xfer *xfer, usb_error_t error)
case USB_ST_TRANSFERRED:
case USB_ST_SETUP:
tr_setup:
if (usbd_xfer_get_and_clr_zlp(xfer))
break;
pc = usbd_xfer_get_frame(xfer, 0);
max = usbd_xfer_max_len(xfer);
if (usb_fifo_get_data(f, pc, 0, max, &actlen, 0)) {
@ -436,10 +439,6 @@ unlpt_open(struct usb_fifo *fifo, int fflags)
return (EBUSY);
}
if (fflags & FREAD) {
/* clear stall first */
mtx_lock(&sc->sc_mtx);
usbd_xfer_set_stall(sc->sc_xfer[ULPT_BULK_DT_RD]);
mtx_unlock(&sc->sc_mtx);
if (usb_fifo_alloc_buffer(fifo,
usbd_xfer_max_len(sc->sc_xfer[ULPT_BULK_DT_RD]),
ULPT_IFQ_MAXLEN)) {
@ -451,7 +450,7 @@ unlpt_open(struct usb_fifo *fifo, int fflags)
if (fflags & FWRITE) {
/* clear stall first */
mtx_lock(&sc->sc_mtx);
usbd_xfer_set_stall(sc->sc_xfer[ULPT_BULK_DT_WR]);
usbd_xfer_set_zlp(sc->sc_xfer[ULPT_BULK_DT_WR]);
mtx_unlock(&sc->sc_mtx);
if (usb_fifo_alloc_buffer(fifo,
usbd_xfer_max_len(sc->sc_xfer[ULPT_BULK_DT_WR]),

View file

@ -382,8 +382,7 @@ umcs7840_attach(device_t dev)
/* clear stall at first run */
mtx_lock(&sc->sc_mtx);
for (subunit = 0; subunit < sc->sc_numports; ++subunit) {
usbd_xfer_set_stall(sc->sc_ports[sc->sc_ucom[subunit].sc_portno].sc_xfer[UMCS7840_BULK_RD_EP]);
usbd_xfer_set_stall(sc->sc_ports[sc->sc_ucom[subunit].sc_portno].sc_xfer[UMCS7840_BULK_WR_EP]);
usbd_xfer_set_zlp(sc->sc_ports[sc->sc_ucom[subunit].sc_portno].sc_xfer[UMCS7840_BULK_WR_EP]);
}
mtx_unlock(&sc->sc_mtx);
@ -916,13 +915,16 @@ umcs7840_write_callbackN(struct usb_xfer *xfer, usb_error_t error, uint8_t subun
case USB_ST_SETUP:
case USB_ST_TRANSFERRED:
tr_setup:
if (usbd_xfer_get_and_clr_zlp(xfer))
break;
pc = usbd_xfer_get_frame(xfer, 0);
if (ucom_get_data(ucom, pc, 0, usbd_xfer_max_len(xfer), &actlen)) {
DPRINTF("Port %d write, has %d bytes\n", ucom->sc_portno, actlen);
usbd_xfer_set_frame_len(xfer, 0, actlen);
usbd_transfer_submit(xfer);
}
return;
break;
default: /* Error */
if (error != USB_ERR_CANCELLED) {
@ -930,7 +932,7 @@ umcs7840_write_callbackN(struct usb_xfer *xfer, usb_error_t error, uint8_t subun
usbd_xfer_set_stall(xfer);
goto tr_setup;
}
return;
break;
}
}

View file

@ -447,13 +447,10 @@ umodem_attach(device_t dev)
goto detach;
}
/* clear stall at first run, if USB host mode */
if (uaa->usb_mode == USB_MODE_HOST) {
mtx_lock(&sc->sc_mtx);
usbd_xfer_set_stall(sc->sc_xfer[UMODEM_BULK_WR]);
usbd_xfer_set_stall(sc->sc_xfer[UMODEM_BULK_RD]);
mtx_unlock(&sc->sc_mtx);
}
/* send a ZLP at first run */
mtx_lock(&sc->sc_mtx);
usbd_xfer_set_zlp(sc->sc_xfer[UMODEM_BULK_WR]);
mtx_unlock(&sc->sc_mtx);
ucom_set_usb_mode(&sc->sc_super_ucom, uaa->usb_mode);
@ -863,13 +860,16 @@ umodem_write_callback(struct usb_xfer *xfer, usb_error_t error)
case USB_ST_SETUP:
case USB_ST_TRANSFERRED:
tr_setup:
if (usbd_xfer_get_and_clr_zlp(xfer))
break;
pc = usbd_xfer_get_frame(xfer, 0);
if (ucom_get_data(&sc->sc_ucom, pc, 0,
UMODEM_BUF_SIZE, &actlen)) {
usbd_xfer_set_frame_len(xfer, 0, actlen);
usbd_transfer_submit(xfer);
}
return;
break;
default: /* Error */
if (error != USB_ERR_CANCELLED) {
@ -877,7 +877,7 @@ umodem_write_callback(struct usb_xfer *xfer, usb_error_t error)
usbd_xfer_set_stall(xfer);
goto tr_setup;
}
return;
break;
}
}

View file

@ -333,8 +333,7 @@ umoscom_attach(device_t dev)
}
/* clear stall at first run */
mtx_lock(&sc->sc_mtx);
usbd_xfer_set_stall(sc->sc_xfer[UMOSCOM_BULK_DT_WR]);
usbd_xfer_set_stall(sc->sc_xfer[UMOSCOM_BULK_DT_RD]);
usbd_xfer_set_zlp(sc->sc_xfer[UMOSCOM_BULK_DT_WR]);
mtx_unlock(&sc->sc_mtx);
error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
@ -639,13 +638,16 @@ umoscom_write_callback(struct usb_xfer *xfer, usb_error_t error)
tr_setup:
DPRINTF("\n");
if (usbd_xfer_get_and_clr_zlp(xfer))
break;
pc = usbd_xfer_get_frame(xfer, 0);
if (ucom_get_data(&sc->sc_ucom, pc, 0,
UMOSCOM_BUFSIZE, &actlen)) {
usbd_xfer_set_frame_len(xfer, 0, actlen);
usbd_transfer_submit(xfer);
}
return;
break;
default: /* Error */
if (error != USB_ERR_CANCELLED) {
@ -654,7 +656,7 @@ umoscom_write_callback(struct usb_xfer *xfer, usb_error_t error)
usbd_xfer_set_stall(xfer);
goto tr_setup;
}
return;
break;
}
}

View file

@ -507,14 +507,12 @@ uplcom_attach(device_t dev)
goto detach;
}
if (sc->sc_chiptype == TYPE_PL2303) {
/* HX variants seem to lock up after a clear stall request. */
mtx_lock(&sc->sc_mtx);
usbd_xfer_set_stall(sc->sc_xfer[UPLCOM_BULK_DT_WR]);
usbd_xfer_set_stall(sc->sc_xfer[UPLCOM_BULK_DT_RD]);
mtx_unlock(&sc->sc_mtx);
} else if (sc->sc_chiptype == TYPE_PL2303HX ||
sc->sc_chiptype == TYPE_PL2303HXD) {
mtx_lock(&sc->sc_mtx);
usbd_xfer_set_zlp(sc->sc_xfer[UPLCOM_BULK_DT_WR]);
mtx_unlock(&sc->sc_mtx);
if (sc->sc_chiptype == TYPE_PL2303HX ||
sc->sc_chiptype == TYPE_PL2303HXD) {
/* reset upstream data pipes */
if (uplcom_pl2303_do(sc->sc_udev, UT_WRITE_VENDOR_DEVICE,
UPLCOM_SET_REQUEST, 8, 0, 0) ||
@ -1094,6 +1092,9 @@ uplcom_write_callback(struct usb_xfer *xfer, usb_error_t error)
case USB_ST_SETUP:
case USB_ST_TRANSFERRED:
tr_setup:
if (usbd_xfer_get_and_clr_zlp(xfer))
break;
pc = usbd_xfer_get_frame(xfer, 0);
if (ucom_get_data(&sc->sc_ucom, pc, 0,
UPLCOM_BULK_BUF_SIZE, &actlen)) {
@ -1102,7 +1103,7 @@ uplcom_write_callback(struct usb_xfer *xfer, usb_error_t error)
usbd_xfer_set_frame_len(xfer, 0, actlen);
usbd_transfer_submit(xfer);
}
return;
break;
default: /* Error */
if (error != USB_ERR_CANCELLED) {
@ -1110,7 +1111,7 @@ uplcom_write_callback(struct usb_xfer *xfer, usb_error_t error)
usbd_xfer_set_stall(xfer);
goto tr_setup;
}
return;
break;
}
}

View file

@ -443,8 +443,7 @@ uslcom_attach(device_t dev)
}
/* clear stall at first run */
mtx_lock(&sc->sc_mtx);
usbd_xfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_WR]);
usbd_xfer_set_stall(sc->sc_xfer[USLCOM_BULK_DT_RD]);
usbd_xfer_set_zlp(sc->sc_xfer[USLCOM_BULK_DT_WR]);
mtx_unlock(&sc->sc_mtx);
sc->sc_partnum = uslcom_get_partnum(sc);
@ -819,6 +818,9 @@ uslcom_write_callback(struct usb_xfer *xfer, usb_error_t error)
case USB_ST_SETUP:
case USB_ST_TRANSFERRED:
tr_setup:
if (usbd_xfer_get_and_clr_zlp(xfer))
break;
pc = usbd_xfer_get_frame(xfer, 0);
if (ucom_get_data(&sc->sc_ucom, pc, 0,
USLCOM_BULK_BUF_SIZE, &actlen)) {
@ -827,7 +829,7 @@ uslcom_write_callback(struct usb_xfer *xfer, usb_error_t error)
usbd_xfer_set_frame_len(xfer, 0, actlen);
usbd_transfer_submit(xfer);
}
return;
break;
default: /* Error */
if (error != USB_ERR_CANCELLED) {
@ -835,7 +837,7 @@ uslcom_write_callback(struct usb_xfer *xfer, usb_error_t error)
usbd_xfer_set_stall(xfer);
goto tr_setup;
}
return;
break;
}
}

View file

@ -318,8 +318,7 @@ uvscom_attach(device_t dev)
/* clear stall at first run */
mtx_lock(&sc->sc_mtx);
usbd_xfer_set_stall(sc->sc_xfer[UVSCOM_BULK_DT_WR]);
usbd_xfer_set_stall(sc->sc_xfer[UVSCOM_BULK_DT_RD]);
usbd_xfer_set_zlp(sc->sc_xfer[UVSCOM_BULK_DT_WR]);
mtx_unlock(&sc->sc_mtx);
error = ucom_attach(&sc->sc_super_ucom, &sc->sc_ucom, 1, sc,
@ -391,13 +390,16 @@ uvscom_write_callback(struct usb_xfer *xfer, usb_error_t error)
case USB_ST_SETUP:
case USB_ST_TRANSFERRED:
tr_setup:
if (usbd_xfer_get_and_clr_zlp(xfer))
break;
pc = usbd_xfer_get_frame(xfer, 0);
if (ucom_get_data(&sc->sc_ucom, pc, 0,
UVSCOM_BULK_BUF_SIZE, &actlen)) {
usbd_xfer_set_frame_len(xfer, 0, actlen);
usbd_transfer_submit(xfer);
}
return;
break;
default: /* Error */
if (error != USB_ERR_CANCELLED) {
@ -405,7 +407,7 @@ uvscom_write_callback(struct usb_xfer *xfer, usb_error_t error)
usbd_xfer_set_stall(xfer);
goto tr_setup;
}
return;
break;
}
}

View file

@ -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);
}

View file

@ -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);