From 4b5f5193848808c0d6c730ce37737538e404c218 Mon Sep 17 00:00:00 2001 From: "Joshua M. Clulow" Date: Mon, 27 Dec 2021 16:08:40 -0800 Subject: [PATCH] illumos: correctly return error conditions --- libusb/os/illumos_usb.c | 98 ++++++++++++++++++++++++++++++++--------- libusb/os/illumos_usb.h | 1 + 2 files changed, 79 insertions(+), 20 deletions(-) diff --git a/libusb/os/illumos_usb.c b/libusb/os/illumos_usb.c index db8dcf75..ec4b2721 100644 --- a/libusb/os/illumos_usb.c +++ b/libusb/os/illumos_usb.c @@ -1186,7 +1186,6 @@ illumos_async_callback(union sigval arg) struct libusb_transfer *xfer = tpriv->transfer; struct usbi_transfer *ixfer = LIBUSB_TRANSFER_TO_USBI_TRANSFER(xfer); struct aiocb *aiocb = &tpriv->aiocb; - int ret; illumos_dev_handle_priv_t *hpriv; uint8_t ep; libusb_device_handle *dev_handle; @@ -1196,23 +1195,20 @@ illumos_async_callback(union sigval arg) return; } - hpriv = usbi_get_device_handle_priv(dev_handle); - ep = illumos_usb_ep_index(xfer->endpoint); + if (aio_error(aiocb) != ECANCELED) { + hpriv = usbi_get_device_handle_priv(dev_handle); + ep = illumos_usb_ep_index(xfer->endpoint); - ret = aio_error(aiocb); - if (ret != 0) { - xfer->status = illumos_usb_get_status(TRANSFER_CTX(xfer), + /* + * Fetch the status for the last command on this endpoint from + * ugen(7D) so that we can translate and report it later. + */ + tpriv->ugen_status = illumos_usb_get_status(TRANSFER_CTX(xfer), hpriv->eps[ep].statfd); } else { - xfer->actual_length = ixfer->transferred = aio_return(aiocb); + tpriv->ugen_status = USB_LC_STAT_NOERROR; } - usb_dump_data(xfer->buffer, xfer->actual_length); - - usbi_dbg(TRANSFER_CTX(xfer), "ret=%d, len=%d, actual_len=%d", - ret, xfer->length, xfer->actual_length); - - /* async notification */ usbi_signal_transfer_completion(ixfer); } @@ -1478,10 +1474,6 @@ illumos_cancel_transfer(struct usbi_transfer *itransfer) if (ret != AIO_CANCELED) { ret = _errno_to_libusb(errno); } else { - /* - * we don't need to call usbi_handle_transfer_cancellation(), - * because we'll handle everything in illumos_async_callback. - */ ret = LIBUSB_SUCCESS; } @@ -1489,10 +1481,76 @@ illumos_cancel_transfer(struct usbi_transfer *itransfer) } int -illumos_handle_transfer_completion(struct usbi_transfer *itransfer) +illumos_handle_transfer_completion(struct usbi_transfer *ixfer) { - return (usbi_handle_transfer_completion(itransfer, - LIBUSB_TRANSFER_COMPLETED)); + illumos_xfer_priv_t *tpriv = usbi_get_transfer_priv(ixfer); + struct libusb_transfer *xfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(ixfer); + struct aiocb *aiocb = &tpriv->aiocb; + int ret; + enum libusb_transfer_status status; + + if ((ret = aio_error(aiocb)) == 0) { + /* + * The command completed. Update the transferred length: + */ + xfer->actual_length = ixfer->transferred = aio_return(aiocb); + + usbi_dbg(TRANSFER_CTX(xfer), "ret=%d, len=%d, actual_len=%d", + ret, xfer->length, xfer->actual_length); + usb_dump_data(xfer->buffer, xfer->actual_length); + + status = LIBUSB_TRANSFER_COMPLETED; + + } else if (ret == ECANCELED) { + /* + * We used aio_cancel() to cancel this; report cancellation to + * libusb so that timeouts can be handled correctly. + */ + usbi_dbg(TRANSFER_CTX(xfer), + "aio cancelled, len=%d, actual_len=%d", + xfer->length, xfer->actual_length); + + status = LIBUSB_TRANSFER_CANCELLED; + + } else { + /* + * Convert the ugen(7D)-level status to a libusb-level status: + */ + switch (tpriv->ugen_status) { + case USB_LC_STAT_TIMEOUT: + status = LIBUSB_TRANSFER_TIMED_OUT; + break; + case USB_LC_STAT_STALL: + status = LIBUSB_TRANSFER_STALL; + break; + case USB_LC_STAT_DISCONNECTED: + status = LIBUSB_TRANSFER_NO_DEVICE; + break; + case USB_LC_STAT_INTERRUPTED: + status = LIBUSB_TRANSFER_CANCELLED; + break; + case USB_LC_STAT_BUFFER_OVERRUN: + /* + * XXX Is this right? (*_DATA_OVERRUN?) + */ + status = LIBUSB_TRANSFER_OVERFLOW; + break; + default: + /* + * Not every ugen(7D) status maps to a specific + * libusb-level failure case. Nonetheless, we must + * report all failures as failures: + */ + status = LIBUSB_TRANSFER_ERROR; + break; + } + } + + if (status == LIBUSB_TRANSFER_CANCELLED) { + return (usbi_handle_transfer_cancellation(ixfer)); + } else { + return (usbi_handle_transfer_completion(ixfer, status)); + } } int diff --git a/libusb/os/illumos_usb.h b/libusb/os/illumos_usb.h index f24c3707..4f3082c8 100644 --- a/libusb/os/illumos_usb.h +++ b/libusb/os/illumos_usb.h @@ -50,6 +50,7 @@ typedef struct illumos_device_handle_priv { typedef struct illumos_transfer_priv { struct aiocb aiocb; struct libusb_transfer *transfer; + int ugen_status; } illumos_xfer_priv_t; struct node_args {