$NetBSD$ Support asynchronous transfers by doing each one in a new thread. This is expensive, but there does not appear to be a good alternative with NetBSD's ugen(4) as is. --- libusb/os/netbsd_usb.c.orig 2014-04-22 12:31:42.000000000 +0000 +++ libusb/os/netbsd_usb.c @@ -21,6 +21,7 @@ #include #include +#include #include #include #include @@ -42,6 +43,13 @@ struct device_priv { struct handle_priv { int pipe[2]; /* for event notification */ int endpoints[USB_MAX_ENDPOINTS]; + pthread_mutex_t lock; /* serialize endpoints */ +}; + +struct transfer_priv { + pthread_t thread; + int (*transfer_method)(struct usbi_transfer *); + enum libusb_transfer_status status; }; /* @@ -86,6 +94,9 @@ static int _cache_active_config_descript static int _sync_control_transfer(struct usbi_transfer *); static int _sync_gen_transfer(struct usbi_transfer *); static int _access_endpoint(struct libusb_transfer *); +static int _async_transfer(struct usbi_transfer *, + int (*)(struct usbi_transfer *)); +static void *_async_transfer_thread(void *); const struct usbi_os_backend netbsd_backend = { "Synchronous NetBSD backend", @@ -130,7 +141,7 @@ const struct usbi_os_backend netbsd_back netbsd_clock_gettime, sizeof(struct device_priv), sizeof(struct handle_priv), - 0, /* transfer_priv_size */ + sizeof(struct transfer_priv), 0, /* add_iso_packet_size */ }; @@ -212,20 +223,44 @@ netbsd_open(struct libusb_device_handle { struct handle_priv *hpriv = (struct handle_priv *)handle->os_priv; struct device_priv *dpriv = (struct device_priv *)handle->dev->os_priv; + int err, ptherr; dpriv->fd = open(dpriv->devnode, O_RDWR); if (dpriv->fd < 0) { dpriv->fd = open(dpriv->devnode, O_RDONLY); - if (dpriv->fd < 0) - return _errno_to_libusb(errno); + if (dpriv->fd < 0) { + err = _errno_to_libusb(errno); + goto fail0; + } } usbi_dbg("open %s: fd %d", dpriv->devnode, dpriv->fd); - if (pipe(hpriv->pipe) < 0) - return _errno_to_libusb(errno); + if (pipe(hpriv->pipe) < 0) { + err = _errno_to_libusb(errno); + goto fail1; + } + + ptherr = pthread_mutex_init(&hpriv->lock, NULL); + if (ptherr) { + err = _errno_to_libusb(ptherr); + goto fail2; + } + + err = usbi_add_pollfd(HANDLE_CTX(handle), hpriv->pipe[0], POLLIN); + if (err) + goto fail3; + + return (0); - return usbi_add_pollfd(HANDLE_CTX(handle), hpriv->pipe[0], POLLIN); +fail3: pthread_mutex_destroy(&hpriv->lock); +fail2: close(hpriv->pipe[0]); + close(hpriv->pipe[1]); + hpriv->pipe[0] = -1; + hpriv->pipe[1] = -1; +fail1: close(dpriv->fd); + dpriv->fd = -1; +fail0: return (err); } void @@ -236,13 +271,17 @@ netbsd_close(struct libusb_device_handle usbi_dbg("close: fd %d", dpriv->fd); - close(dpriv->fd); - dpriv->fd = -1; - usbi_remove_pollfd(HANDLE_CTX(handle), hpriv->pipe[0]); + pthread_mutex_destroy(&hpriv->lock); + close(hpriv->pipe[0]); close(hpriv->pipe[1]); + hpriv->pipe[0] = -1; + hpriv->pipe[1] = -1; + + close(dpriv->fd); + dpriv->fd = -1; } int @@ -442,7 +481,7 @@ netbsd_submit_transfer(struct usbi_trans switch (transfer->type) { case LIBUSB_TRANSFER_TYPE_CONTROL: - err = _sync_control_transfer(itransfer); + err = _async_transfer(itransfer, _sync_control_transfer); break; case LIBUSB_TRANSFER_TYPE_ISOCHRONOUS: if (IS_XFEROUT(transfer)) { @@ -450,7 +489,7 @@ netbsd_submit_transfer(struct usbi_trans err = LIBUSB_ERROR_NOT_SUPPORTED; break; } - err = _sync_gen_transfer(itransfer); + err = _async_transfer(itransfer, _sync_gen_transfer); break; case LIBUSB_TRANSFER_TYPE_BULK: case LIBUSB_TRANSFER_TYPE_INTERRUPT: @@ -459,7 +498,7 @@ netbsd_submit_transfer(struct usbi_trans err = LIBUSB_ERROR_NOT_SUPPORTED; break; } - err = _sync_gen_transfer(itransfer); + err = _async_transfer(itransfer, _sync_gen_transfer); break; case LIBUSB_TRANSFER_TYPE_BULK_STREAM: err = LIBUSB_ERROR_NOT_SUPPORTED; @@ -469,9 +508,6 @@ netbsd_submit_transfer(struct usbi_trans if (err) return (err); - if (write(hpriv->pipe[1], &itransfer, sizeof(itransfer)) < 0) - return _errno_to_libusb(errno); - return (LIBUSB_SUCCESS); } @@ -498,6 +534,7 @@ netbsd_handle_events(struct libusb_conte struct libusb_device_handle *handle; struct handle_priv *hpriv = NULL; struct usbi_transfer *itransfer; + struct transfer_priv *tpriv; struct pollfd *pollfd; int i, err = 0; @@ -524,7 +561,7 @@ netbsd_handle_events(struct libusb_conte if (NULL == hpriv) { usbi_dbg("fd %d is not an event pipe!", pollfd->fd); - err = ENOENT; + err = _errno_to_libusb(ENOENT); break; } @@ -535,20 +572,18 @@ netbsd_handle_events(struct libusb_conte } if (read(hpriv->pipe[0], &itransfer, sizeof(itransfer)) < 0) { - err = errno; + err = _errno_to_libusb(errno); break; } + tpriv = usbi_transfer_get_os_priv(itransfer); if ((err = usbi_handle_transfer_completion(itransfer, - LIBUSB_TRANSFER_COMPLETED))) + tpriv->status)) != 0) break; } pthread_mutex_unlock(&ctx->open_devs_lock); - if (err) - return _errno_to_libusb(err); - - return (LIBUSB_SUCCESS); + return (err); } int @@ -625,7 +660,7 @@ _cache_active_config_descriptor(struct l return (0); } -int +static int _sync_control_transfer(struct usbi_transfer *itransfer) { struct libusb_transfer *transfer; @@ -646,9 +681,9 @@ _sync_control_transfer(struct usbi_trans req.ucr_request.bmRequestType = setup->bmRequestType; req.ucr_request.bRequest = setup->bRequest; /* Don't use USETW, libusb already deals with the endianness */ - (*(uint16_t *)req.ucr_request.wValue) = setup->wValue; - (*(uint16_t *)req.ucr_request.wIndex) = setup->wIndex; - (*(uint16_t *)req.ucr_request.wLength) = setup->wLength; + memcpy(req.ucr_request.wValue, &setup->wValue, 2); + memcpy(req.ucr_request.wIndex, &setup->wIndex, 2); + memcpy(req.ucr_request.wLength, &setup->wLength, 2); req.ucr_data = transfer->buffer + LIBUSB_CONTROL_SETUP_SIZE; if ((transfer->flags & LIBUSB_TRANSFER_SHORT_NOT_OK) == 0) @@ -684,6 +719,10 @@ _access_endpoint(struct libusb_transfer usbi_dbg("endpoint %d mode %d", endpt, mode); + if (hpriv->endpoints[endpt] >= 0) + return hpriv->endpoints[endpt]; + + pthread_mutex_lock(&hpriv->lock); if (hpriv->endpoints[endpt] < 0) { /* Pick the right node given the control one */ strlcpy(devnode, dpriv->devnode, sizeof(devnode)); @@ -697,11 +736,12 @@ _access_endpoint(struct libusb_transfer hpriv->endpoints[endpt] = fd; } + pthread_mutex_unlock(&hpriv->lock); return (hpriv->endpoints[endpt]); } -int +static int _sync_gen_transfer(struct usbi_transfer *itransfer) { struct libusb_transfer *transfer; @@ -736,3 +776,63 @@ _sync_gen_transfer(struct usbi_transfer return (0); } + +static int +_async_transfer(struct usbi_transfer *itransfer, + int (*transfer_method)(struct usbi_transfer *)) +{ + pthread_attr_t attr; + struct transfer_priv *tpriv; + int ptherr; + + tpriv = usbi_transfer_get_os_priv(itransfer); + + /* Create a thread to do the transfer. */ + tpriv->transfer_method = transfer_method; + ptherr = pthread_attr_init(&attr); + if (ptherr) + goto fail0; + ptherr = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + if (ptherr) + goto fail1; + ptherr = pthread_create(&tpriv->thread, &attr, _async_transfer_thread, + itransfer); + if (ptherr) + goto fail1; + + /* Success! */ + pthread_attr_destroy(&attr); + return (0); + +fail1: pthread_attr_destroy(&attr); +fail0: return _errno_to_libusb(ptherr); +} + +static void * +_async_transfer_thread(void *cookie) +{ + struct usbi_transfer *itransfer = cookie; + struct libusb_transfer *transfer; + struct transfer_priv *tpriv; + struct handle_priv *hpriv; + int err; + + transfer = USBI_TRANSFER_TO_LIBUSB_TRANSFER(itransfer); + tpriv = usbi_transfer_get_os_priv(itransfer); + hpriv = (struct handle_priv *)transfer->dev_handle->os_priv; + + /* Attempt the transfer. */ + err = (*tpriv->transfer_method)(itransfer); + if (err) { + tpriv->status = LIBUSB_TRANSFER_ERROR; + goto out; + } + tpriv->status = LIBUSB_TRANSFER_COMPLETED; + +out: + /* Can't do anything if this fails. */ + if (write(hpriv->pipe[1], &itransfer, sizeof(itransfer)) < 0) + usbi_err(TRANSFER_CTX(transfer), "write failed (%d): %s", + errno, strerror(errno)); + return (NULL); +}