Index: stable/11/lib/libusb/libusb20.c =================================================================== --- stable/11/lib/libusb/libusb20.c (revision 331770) +++ stable/11/lib/libusb/libusb20.c (revision 331771) @@ -1,1355 +1,1373 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008-2009 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 * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifdef LIBUSB_GLOBAL_INCLUDE_FILE #include LIBUSB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #endif #include "libusb20.h" #include "libusb20_desc.h" #include "libusb20_int.h" static int dummy_int(void) { return (LIBUSB20_ERROR_NOT_SUPPORTED); } static void dummy_void(void) { return; } static void dummy_callback(struct libusb20_transfer *xfer) { ; /* style fix */ switch (libusb20_tr_get_status(xfer)) { case LIBUSB20_TRANSFER_START: libusb20_tr_submit(xfer); break; default: /* complete or error */ break; } return; } #define dummy_get_config_desc_full (void *)dummy_int #define dummy_get_config_index (void *)dummy_int #define dummy_set_config_index (void *)dummy_int #define dummy_set_alt_index (void *)dummy_int #define dummy_reset_device (void *)dummy_int #define dummy_check_connected (void *)dummy_int #define dummy_set_power_mode (void *)dummy_int #define dummy_get_power_mode (void *)dummy_int -#define dummy_get_port_path (void *)dummy_int #define dummy_get_power_usage (void *)dummy_int #define dummy_kernel_driver_active (void *)dummy_int #define dummy_detach_kernel_driver (void *)dummy_int #define dummy_do_request_sync (void *)dummy_int #define dummy_tr_open (void *)dummy_int #define dummy_tr_close (void *)dummy_int #define dummy_tr_clear_stall_sync (void *)dummy_int #define dummy_process (void *)dummy_int #define dummy_dev_info (void *)dummy_int #define dummy_dev_get_iface_driver (void *)dummy_int #define dummy_tr_submit (void *)dummy_void #define dummy_tr_cancel_async (void *)dummy_void static const struct libusb20_device_methods libusb20_dummy_methods = { LIBUSB20_DEVICE(LIBUSB20_DECLARE, dummy) }; void libusb20_tr_callback_wrapper(struct libusb20_transfer *xfer) { ; /* style fix */ repeat: if (!xfer->is_pending) { xfer->status = LIBUSB20_TRANSFER_START; } else { xfer->is_pending = 0; } xfer->callback(xfer); if (xfer->is_restart) { xfer->is_restart = 0; goto repeat; } if (xfer->is_draining && (!xfer->is_pending)) { xfer->is_draining = 0; xfer->status = LIBUSB20_TRANSFER_DRAINED; xfer->callback(xfer); } return; } int libusb20_tr_close(struct libusb20_transfer *xfer) { int error; if (!xfer->is_opened) { return (LIBUSB20_ERROR_OTHER); } error = xfer->pdev->methods->tr_close(xfer); if (xfer->pLength) { free(xfer->pLength); } if (xfer->ppBuffer) { free(xfer->ppBuffer); } /* reset variable fields in case the transfer is opened again */ xfer->priv_sc0 = NULL; xfer->priv_sc1 = NULL; xfer->is_opened = 0; xfer->is_pending = 0; xfer->is_cancel = 0; xfer->is_draining = 0; xfer->is_restart = 0; xfer->status = 0; xfer->flags = 0; xfer->nFrames = 0; xfer->aFrames = 0; xfer->timeout = 0; xfer->maxFrames = 0; xfer->maxTotalLength = 0; xfer->maxPacketLen = 0; return (error); } int libusb20_tr_open(struct libusb20_transfer *xfer, uint32_t MaxBufSize, uint32_t MaxFrameCount, uint8_t ep_no) { return (libusb20_tr_open_stream(xfer, MaxBufSize, MaxFrameCount, ep_no, 0)); } int libusb20_tr_open_stream(struct libusb20_transfer *xfer, uint32_t MaxBufSize, uint32_t MaxFrameCount, uint8_t ep_no, uint16_t stream_id) { uint32_t size; uint8_t pre_scale; int error; if (xfer->is_opened) return (LIBUSB20_ERROR_BUSY); if (MaxFrameCount & LIBUSB20_MAX_FRAME_PRE_SCALE) { MaxFrameCount &= ~LIBUSB20_MAX_FRAME_PRE_SCALE; /* * The kernel can setup 8 times more frames when * pre-scaling ISOCHRONOUS transfers. Make sure the * length and pointer buffers are big enough: */ MaxFrameCount *= 8; pre_scale = 1; } else { pre_scale = 0; } if (MaxFrameCount == 0) return (LIBUSB20_ERROR_INVALID_PARAM); xfer->maxFrames = MaxFrameCount; size = MaxFrameCount * sizeof(xfer->pLength[0]); xfer->pLength = malloc(size); if (xfer->pLength == NULL) { return (LIBUSB20_ERROR_NO_MEM); } memset(xfer->pLength, 0, size); size = MaxFrameCount * sizeof(xfer->ppBuffer[0]); xfer->ppBuffer = malloc(size); if (xfer->ppBuffer == NULL) { free(xfer->pLength); return (LIBUSB20_ERROR_NO_MEM); } memset(xfer->ppBuffer, 0, size); if (pre_scale) { error = xfer->pdev->methods->tr_open(xfer, MaxBufSize, MaxFrameCount / 8, ep_no, stream_id, 1); } else { error = xfer->pdev->methods->tr_open(xfer, MaxBufSize, MaxFrameCount, ep_no, stream_id, 0); } if (error) { free(xfer->ppBuffer); free(xfer->pLength); } else { xfer->is_opened = 1; } return (error); } struct libusb20_transfer * libusb20_tr_get_pointer(struct libusb20_device *pdev, uint16_t trIndex) { if (trIndex >= pdev->nTransfer) { return (NULL); } return (pdev->pTransfer + trIndex); } uint32_t libusb20_tr_get_actual_frames(struct libusb20_transfer *xfer) { return (xfer->aFrames); } uint16_t libusb20_tr_get_time_complete(struct libusb20_transfer *xfer) { return (xfer->timeComplete); } uint32_t libusb20_tr_get_actual_length(struct libusb20_transfer *xfer) { uint32_t x; uint32_t actlen = 0; for (x = 0; x != xfer->aFrames; x++) { actlen += xfer->pLength[x]; } return (actlen); } uint32_t libusb20_tr_get_max_frames(struct libusb20_transfer *xfer) { return (xfer->maxFrames); } uint32_t libusb20_tr_get_max_packet_length(struct libusb20_transfer *xfer) { /* * Special Case NOTE: If the packet multiplier is non-zero for * High Speed USB, the value returned is equal to * "wMaxPacketSize * multiplier" ! */ return (xfer->maxPacketLen); } uint32_t libusb20_tr_get_max_total_length(struct libusb20_transfer *xfer) { return (xfer->maxTotalLength); } uint8_t libusb20_tr_get_status(struct libusb20_transfer *xfer) { return (xfer->status); } uint8_t libusb20_tr_pending(struct libusb20_transfer *xfer) { return (xfer->is_pending); } void * libusb20_tr_get_priv_sc0(struct libusb20_transfer *xfer) { return (xfer->priv_sc0); } void * libusb20_tr_get_priv_sc1(struct libusb20_transfer *xfer) { return (xfer->priv_sc1); } void libusb20_tr_stop(struct libusb20_transfer *xfer) { if (!xfer->is_opened) { /* transfer is not opened */ return; } if (!xfer->is_pending) { /* transfer not pending */ return; } if (xfer->is_cancel) { /* already cancelling */ return; } xfer->is_cancel = 1; /* we are cancelling */ xfer->pdev->methods->tr_cancel_async(xfer); return; } void libusb20_tr_drain(struct libusb20_transfer *xfer) { if (!xfer->is_opened) { /* transfer is not opened */ return; } /* make sure that we are cancelling */ libusb20_tr_stop(xfer); if (xfer->is_pending) { xfer->is_draining = 1; } return; } void libusb20_tr_clear_stall_sync(struct libusb20_transfer *xfer) { xfer->pdev->methods->tr_clear_stall_sync(xfer); return; } void libusb20_tr_set_buffer(struct libusb20_transfer *xfer, void *buffer, uint16_t frIndex) { xfer->ppBuffer[frIndex] = libusb20_pass_ptr(buffer); return; } void libusb20_tr_set_callback(struct libusb20_transfer *xfer, libusb20_tr_callback_t *cb) { xfer->callback = cb; return; } void libusb20_tr_set_flags(struct libusb20_transfer *xfer, uint8_t flags) { xfer->flags = flags; return; } uint32_t libusb20_tr_get_length(struct libusb20_transfer *xfer, uint16_t frIndex) { return (xfer->pLength[frIndex]); } void libusb20_tr_set_length(struct libusb20_transfer *xfer, uint32_t length, uint16_t frIndex) { xfer->pLength[frIndex] = length; return; } void libusb20_tr_set_priv_sc0(struct libusb20_transfer *xfer, void *sc0) { xfer->priv_sc0 = sc0; return; } void libusb20_tr_set_priv_sc1(struct libusb20_transfer *xfer, void *sc1) { xfer->priv_sc1 = sc1; return; } void libusb20_tr_set_timeout(struct libusb20_transfer *xfer, uint32_t timeout) { xfer->timeout = timeout; return; } void libusb20_tr_set_total_frames(struct libusb20_transfer *xfer, uint32_t nFrames) { if (nFrames > xfer->maxFrames) { /* should not happen */ nFrames = xfer->maxFrames; } xfer->nFrames = nFrames; return; } void libusb20_tr_setup_bulk(struct libusb20_transfer *xfer, void *pBuf, uint32_t length, uint32_t timeout) { xfer->ppBuffer[0] = libusb20_pass_ptr(pBuf); xfer->pLength[0] = length; xfer->timeout = timeout; xfer->nFrames = 1; return; } void libusb20_tr_setup_control(struct libusb20_transfer *xfer, void *psetup, void *pBuf, uint32_t timeout) { uint16_t len; xfer->ppBuffer[0] = libusb20_pass_ptr(psetup); xfer->pLength[0] = 8; /* fixed */ xfer->timeout = timeout; len = ((uint8_t *)psetup)[6] | (((uint8_t *)psetup)[7] << 8); if (len != 0) { xfer->nFrames = 2; xfer->ppBuffer[1] = libusb20_pass_ptr(pBuf); xfer->pLength[1] = len; } else { xfer->nFrames = 1; } return; } void libusb20_tr_setup_intr(struct libusb20_transfer *xfer, void *pBuf, uint32_t length, uint32_t timeout) { xfer->ppBuffer[0] = libusb20_pass_ptr(pBuf); xfer->pLength[0] = length; xfer->timeout = timeout; xfer->nFrames = 1; return; } void libusb20_tr_setup_isoc(struct libusb20_transfer *xfer, void *pBuf, uint32_t length, uint16_t frIndex) { if (frIndex >= xfer->maxFrames) { /* should not happen */ return; } xfer->ppBuffer[frIndex] = libusb20_pass_ptr(pBuf); xfer->pLength[frIndex] = length; return; } uint8_t libusb20_tr_bulk_intr_sync(struct libusb20_transfer *xfer, void *pbuf, uint32_t length, uint32_t *pactlen, uint32_t timeout) { struct libusb20_device *pdev = xfer->pdev; uint32_t transfer_max; uint32_t transfer_act; uint8_t retval; /* set some sensible default value */ if (pactlen != NULL) *pactlen = 0; /* check for error condition */ if (libusb20_tr_pending(xfer)) return (LIBUSB20_ERROR_OTHER); do { /* compute maximum transfer length */ transfer_max = libusb20_tr_get_max_total_length(xfer); if (transfer_max > length) transfer_max = length; /* setup bulk or interrupt transfer */ libusb20_tr_setup_bulk(xfer, pbuf, transfer_max, timeout); /* start the transfer */ libusb20_tr_start(xfer); /* wait for transfer completion */ while (libusb20_dev_process(pdev) == 0) { if (libusb20_tr_pending(xfer) == 0) break; libusb20_dev_wait_process(pdev, -1); } transfer_act = libusb20_tr_get_actual_length(xfer); /* update actual length, if any */ if (pactlen != NULL) pactlen[0] += transfer_act; /* check transfer status */ retval = libusb20_tr_get_status(xfer); if (retval) break; /* check for short transfer */ if (transfer_act != transfer_max) break; /* update buffer pointer and length */ pbuf = ((uint8_t *)pbuf) + transfer_max; length = length - transfer_max; } while (length != 0); return (retval); } void libusb20_tr_submit(struct libusb20_transfer *xfer) { if (!xfer->is_opened) { /* transfer is not opened */ return; } if (xfer->is_pending) { /* should not happen */ return; } xfer->is_pending = 1; /* we are pending */ xfer->is_cancel = 0; /* not cancelling */ xfer->is_restart = 0; /* not restarting */ xfer->pdev->methods->tr_submit(xfer); return; } void libusb20_tr_start(struct libusb20_transfer *xfer) { if (!xfer->is_opened) { /* transfer is not opened */ return; } if (xfer->is_pending) { if (xfer->is_cancel) { /* cancelling - restart */ xfer->is_restart = 1; } /* transfer not pending */ return; } /* get into the callback */ libusb20_tr_callback_wrapper(xfer); return; } /* USB device operations */ int libusb20_dev_close(struct libusb20_device *pdev) { struct libusb20_transfer *xfer; uint16_t x; int error = 0; if (!pdev->is_opened) { return (LIBUSB20_ERROR_OTHER); } for (x = 0; x != pdev->nTransfer; x++) { xfer = pdev->pTransfer + x; if (!xfer->is_opened) { /* transfer is not opened */ continue; } libusb20_tr_drain(xfer); libusb20_tr_close(xfer); } if (pdev->pTransfer != NULL) { free(pdev->pTransfer); pdev->pTransfer = NULL; } error = pdev->beMethods->close_device(pdev); pdev->methods = &libusb20_dummy_methods; pdev->is_opened = 0; /* * The following variable is only used by the libusb v0.1 * compat layer: */ pdev->claimed_interface = 0; /* * The following variable is only used by the libusb v1.0 * compat layer: */ pdev->auto_detach = 0; return (error); } int libusb20_dev_detach_kernel_driver(struct libusb20_device *pdev, uint8_t ifaceIndex) { int error; error = pdev->methods->detach_kernel_driver(pdev, ifaceIndex); return (error); } struct LIBUSB20_DEVICE_DESC_DECODED * libusb20_dev_get_device_desc(struct libusb20_device *pdev) { return (&(pdev->ddesc)); } int libusb20_dev_get_fd(struct libusb20_device *pdev) { return (pdev->file); } int libusb20_dev_kernel_driver_active(struct libusb20_device *pdev, uint8_t ifaceIndex) { int error; error = pdev->methods->kernel_driver_active(pdev, ifaceIndex); return (error); } int libusb20_dev_open(struct libusb20_device *pdev, uint16_t nTransferMax) { struct libusb20_transfer *xfer; uint32_t size; uint16_t x; int error; if (pdev->is_opened) { return (LIBUSB20_ERROR_BUSY); } if (nTransferMax >= 256) { return (LIBUSB20_ERROR_INVALID_PARAM); } else if (nTransferMax != 0) { size = sizeof(pdev->pTransfer[0]) * nTransferMax; pdev->pTransfer = malloc(size); if (pdev->pTransfer == NULL) { return (LIBUSB20_ERROR_NO_MEM); } memset(pdev->pTransfer, 0, size); } /* initialise all transfers */ for (x = 0; x != nTransferMax; x++) { xfer = pdev->pTransfer + x; xfer->pdev = pdev; xfer->trIndex = x; xfer->callback = &dummy_callback; } /* set "nTransfer" early */ pdev->nTransfer = nTransferMax; error = pdev->beMethods->open_device(pdev, nTransferMax); if (error) { if (pdev->pTransfer != NULL) { free(pdev->pTransfer); pdev->pTransfer = NULL; } pdev->file = -1; pdev->file_ctrl = -1; pdev->nTransfer = 0; } else { pdev->is_opened = 1; } return (error); } int libusb20_dev_reset(struct libusb20_device *pdev) { int error; error = pdev->methods->reset_device(pdev); return (error); } int libusb20_dev_check_connected(struct libusb20_device *pdev) { int error; error = pdev->methods->check_connected(pdev); return (error); } int libusb20_dev_set_power_mode(struct libusb20_device *pdev, uint8_t power_mode) { int error; error = pdev->methods->set_power_mode(pdev, power_mode); return (error); } uint8_t libusb20_dev_get_power_mode(struct libusb20_device *pdev) { int error; uint8_t power_mode; error = pdev->methods->get_power_mode(pdev, &power_mode); if (error) power_mode = LIBUSB20_POWER_ON; /* fake power mode */ return (power_mode); } int libusb20_dev_get_port_path(struct libusb20_device *pdev, uint8_t *buf, uint8_t bufsize) { - return (pdev->methods->get_port_path(pdev, buf, bufsize)); + + if (pdev->port_level == 0) { + /* + * Fallback for backends without port path: + */ + if (bufsize < 2) + return (LIBUSB20_ERROR_OVERFLOW); + buf[0] = pdev->parent_address; + buf[1] = pdev->parent_port; + return (2); + } + + /* check if client buffer is too small */ + if (pdev->port_level > bufsize) + return (LIBUSB20_ERROR_OVERFLOW); + + /* copy port number information */ + memcpy(buf, pdev->port_path, pdev->port_level); + + return (pdev->port_level); /* success */ } uint16_t libusb20_dev_get_power_usage(struct libusb20_device *pdev) { int error; uint16_t power_usage; error = pdev->methods->get_power_usage(pdev, &power_usage); if (error) power_usage = 0; return (power_usage); } int libusb20_dev_set_alt_index(struct libusb20_device *pdev, uint8_t ifaceIndex, uint8_t altIndex) { int error; error = pdev->methods->set_alt_index(pdev, ifaceIndex, altIndex); return (error); } int libusb20_dev_set_config_index(struct libusb20_device *pdev, uint8_t configIndex) { int error; error = pdev->methods->set_config_index(pdev, configIndex); return (error); } int libusb20_dev_request_sync(struct libusb20_device *pdev, struct LIBUSB20_CONTROL_SETUP_DECODED *setup, void *data, uint16_t *pactlen, uint32_t timeout, uint8_t flags) { int error; error = pdev->methods->do_request_sync(pdev, setup, data, pactlen, timeout, flags); return (error); } int libusb20_dev_req_string_sync(struct libusb20_device *pdev, uint8_t str_index, uint16_t langid, void *ptr, uint16_t len) { struct LIBUSB20_CONTROL_SETUP_DECODED req; int error; /* make sure memory is initialised */ memset(ptr, 0, len); if (len < 4) { /* invalid length */ return (LIBUSB20_ERROR_INVALID_PARAM); } LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &req); /* * We need to read the USB string in two steps else some USB * devices will complain. */ req.bmRequestType = LIBUSB20_REQUEST_TYPE_STANDARD | LIBUSB20_RECIPIENT_DEVICE | LIBUSB20_ENDPOINT_IN; req.bRequest = LIBUSB20_REQUEST_GET_DESCRIPTOR; req.wValue = (LIBUSB20_DT_STRING << 8) | str_index; req.wIndex = langid; req.wLength = 4; /* bytes */ error = libusb20_dev_request_sync(pdev, &req, ptr, NULL, 1000, LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK); if (error) { return (error); } req.wLength = *(uint8_t *)ptr; /* bytes */ if (req.wLength > len) { /* partial string read */ req.wLength = len; } error = libusb20_dev_request_sync(pdev, &req, ptr, NULL, 1000, LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK); if (error) { return (error); } if (((uint8_t *)ptr)[1] != LIBUSB20_DT_STRING) { return (LIBUSB20_ERROR_OTHER); } return (0); /* success */ } int libusb20_dev_req_string_simple_sync(struct libusb20_device *pdev, uint8_t str_index, void *ptr, uint16_t len) { char *buf; int error; uint16_t langid; uint16_t n; uint16_t i; uint16_t c; uint8_t temp[255]; uint8_t swap; /* the following code derives from the FreeBSD USB kernel */ if ((len < 1) || (ptr == NULL)) { /* too short buffer */ return (LIBUSB20_ERROR_INVALID_PARAM); } error = libusb20_dev_req_string_sync(pdev, 0, 0, temp, sizeof(temp)); if (error < 0) { *(uint8_t *)ptr = 0; /* zero terminate */ return (error); } langid = temp[2] | (temp[3] << 8); error = libusb20_dev_req_string_sync(pdev, str_index, langid, temp, sizeof(temp)); if (error < 0) { *(uint8_t *)ptr = 0; /* zero terminate */ return (error); } if (temp[0] < 2) { /* string length is too short */ *(uint8_t *)ptr = 0; /* zero terminate */ return (LIBUSB20_ERROR_OTHER); } /* reserve one byte for terminating zero */ len--; /* find maximum length */ n = (temp[0] / 2) - 1; if (n > len) { n = len; } /* reset swap state */ swap = 3; /* setup output buffer pointer */ buf = ptr; /* convert and filter */ for (i = 0; (i != n); i++) { c = temp[(2 * i) + 2] | (temp[(2 * i) + 3] << 8); /* convert from Unicode, handle buggy strings */ if (((c & 0xff00) == 0) && (swap & 1)) { /* Little Endian, default */ *buf = c; swap = 1; } else if (((c & 0x00ff) == 0) && (swap & 2)) { /* Big Endian */ *buf = c >> 8; swap = 2; } else { /* skip invalid character */ continue; } /* * Filter by default - we don't allow greater and less than * signs because they might confuse the dmesg printouts! */ if ((*buf == '<') || (*buf == '>') || (!isprint(*buf))) { /* skip invalid character */ continue; } buf++; } *buf = 0; /* zero terminate string */ return (0); } struct libusb20_config * libusb20_dev_alloc_config(struct libusb20_device *pdev, uint8_t configIndex) { struct libusb20_config *retval = NULL; uint8_t *ptr; uint16_t len; uint8_t do_close; int error; if (!pdev->is_opened) { error = libusb20_dev_open(pdev, 0); if (error) { return (NULL); } do_close = 1; } else { do_close = 0; } error = pdev->methods->get_config_desc_full(pdev, &ptr, &len, configIndex); if (error) { goto done; } /* parse new config descriptor */ retval = libusb20_parse_config_desc(ptr); /* free config descriptor */ free(ptr); done: if (do_close) { error = libusb20_dev_close(pdev); } return (retval); } struct libusb20_device * libusb20_dev_alloc(void) { struct libusb20_device *pdev; pdev = malloc(sizeof(*pdev)); if (pdev == NULL) { return (NULL); } memset(pdev, 0, sizeof(*pdev)); pdev->file = -1; pdev->file_ctrl = -1; pdev->methods = &libusb20_dummy_methods; return (pdev); } uint8_t libusb20_dev_get_config_index(struct libusb20_device *pdev) { int error; uint8_t cfg_index; uint8_t do_close; if (!pdev->is_opened) { error = libusb20_dev_open(pdev, 0); if (error == 0) { do_close = 1; } else { do_close = 0; } } else { do_close = 0; } error = pdev->methods->get_config_index(pdev, &cfg_index); if (error) cfg_index = 0xFF; /* current config index */ if (do_close) { if (libusb20_dev_close(pdev)) { /* ignore */ } } return (cfg_index); } uint8_t libusb20_dev_get_mode(struct libusb20_device *pdev) { return (pdev->usb_mode); } uint8_t libusb20_dev_get_speed(struct libusb20_device *pdev) { return (pdev->usb_speed); } /* if this function returns an error, the device is gone */ int libusb20_dev_process(struct libusb20_device *pdev) { int error; error = pdev->methods->process(pdev); return (error); } void libusb20_dev_wait_process(struct libusb20_device *pdev, int timeout) { struct pollfd pfd[1]; if (!pdev->is_opened) { return; } pfd[0].fd = pdev->file; pfd[0].events = (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM); pfd[0].revents = 0; if (poll(pfd, 1, timeout)) { /* ignore any error */ } return; } void libusb20_dev_free(struct libusb20_device *pdev) { if (pdev == NULL) { /* be NULL safe */ return; } if (pdev->is_opened) { if (libusb20_dev_close(pdev)) { /* ignore any errors */ } } free(pdev); return; } int libusb20_dev_get_info(struct libusb20_device *pdev, struct usb_device_info *pinfo) { if (pinfo == NULL) return (LIBUSB20_ERROR_INVALID_PARAM); return (pdev->beMethods->dev_get_info(pdev, pinfo)); } const char * libusb20_dev_get_backend_name(struct libusb20_device *pdev) { return (pdev->beMethods->get_backend_name()); } const char * libusb20_dev_get_desc(struct libusb20_device *pdev) { return (pdev->usb_desc); } void libusb20_dev_set_debug(struct libusb20_device *pdev, int debug) { pdev->debug = debug; return; } int libusb20_dev_get_debug(struct libusb20_device *pdev) { return (pdev->debug); } uint8_t libusb20_dev_get_address(struct libusb20_device *pdev) { return (pdev->device_address); } uint8_t libusb20_dev_get_parent_address(struct libusb20_device *pdev) { return (pdev->parent_address); } uint8_t libusb20_dev_get_parent_port(struct libusb20_device *pdev) { return (pdev->parent_port); } uint8_t libusb20_dev_get_bus_number(struct libusb20_device *pdev) { return (pdev->bus_number); } int libusb20_dev_get_iface_desc(struct libusb20_device *pdev, uint8_t iface_index, char *buf, uint8_t len) { if ((buf == NULL) || (len == 0)) return (LIBUSB20_ERROR_INVALID_PARAM); buf[0] = 0; /* set default string value */ return (pdev->beMethods->dev_get_iface_desc( pdev, iface_index, buf, len)); } /* USB backend operations */ int libusb20_be_get_dev_quirk(struct libusb20_backend *pbe, uint16_t quirk_index, struct libusb20_quirk *pq) { return (pbe->methods->root_get_dev_quirk(pbe, quirk_index, pq)); } int libusb20_be_get_quirk_name(struct libusb20_backend *pbe, uint16_t quirk_index, struct libusb20_quirk *pq) { return (pbe->methods->root_get_quirk_name(pbe, quirk_index, pq)); } int libusb20_be_add_dev_quirk(struct libusb20_backend *pbe, struct libusb20_quirk *pq) { return (pbe->methods->root_add_dev_quirk(pbe, pq)); } int libusb20_be_remove_dev_quirk(struct libusb20_backend *pbe, struct libusb20_quirk *pq) { return (pbe->methods->root_remove_dev_quirk(pbe, pq)); } int libusb20_be_set_template(struct libusb20_backend *pbe, int temp) { return (pbe->methods->root_set_template(pbe, temp)); } int libusb20_be_get_template(struct libusb20_backend *pbe, int *ptemp) { int temp; if (ptemp == NULL) ptemp = &temp; return (pbe->methods->root_get_template(pbe, ptemp)); } struct libusb20_device * libusb20_be_device_foreach(struct libusb20_backend *pbe, struct libusb20_device *pdev) { if (pbe == NULL) { pdev = NULL; } else if (pdev == NULL) { pdev = TAILQ_FIRST(&(pbe->usb_devs)); } else { pdev = TAILQ_NEXT(pdev, dev_entry); } return (pdev); } struct libusb20_backend * libusb20_be_alloc(const struct libusb20_backend_methods *methods) { struct libusb20_backend *pbe; pbe = malloc(sizeof(*pbe)); if (pbe == NULL) { return (NULL); } memset(pbe, 0, sizeof(*pbe)); TAILQ_INIT(&(pbe->usb_devs)); pbe->methods = methods; /* set backend methods */ /* do the initial device scan */ if (pbe->methods->init_backend) { pbe->methods->init_backend(pbe); } return (pbe); } struct libusb20_backend * libusb20_be_alloc_linux(void) { return (NULL); } struct libusb20_backend * libusb20_be_alloc_ugen20(void) { return (libusb20_be_alloc(&libusb20_ugen20_backend)); } struct libusb20_backend * libusb20_be_alloc_default(void) { struct libusb20_backend *pbe; #ifdef __linux__ pbe = libusb20_be_alloc_linux(); if (pbe) { return (pbe); } #endif pbe = libusb20_be_alloc_ugen20(); if (pbe) { return (pbe); } return (NULL); /* no backend found */ } void libusb20_be_free(struct libusb20_backend *pbe) { struct libusb20_device *pdev; if (pbe == NULL) { /* be NULL safe */ return; } while ((pdev = libusb20_be_device_foreach(pbe, NULL))) { libusb20_be_dequeue_device(pbe, pdev); libusb20_dev_free(pdev); } if (pbe->methods->exit_backend) { pbe->methods->exit_backend(pbe); } /* free backend */ free(pbe); } void libusb20_be_enqueue_device(struct libusb20_backend *pbe, struct libusb20_device *pdev) { pdev->beMethods = pbe->methods; /* copy backend methods */ TAILQ_INSERT_TAIL(&(pbe->usb_devs), pdev, dev_entry); } void libusb20_be_dequeue_device(struct libusb20_backend *pbe, struct libusb20_device *pdev) { TAILQ_REMOVE(&(pbe->usb_devs), pdev, dev_entry); } const char * libusb20_strerror(int code) { switch (code) { case LIBUSB20_SUCCESS: return ("Success"); case LIBUSB20_ERROR_IO: return ("I/O error"); case LIBUSB20_ERROR_INVALID_PARAM: return ("Invalid parameter"); case LIBUSB20_ERROR_ACCESS: return ("Permissions error"); case LIBUSB20_ERROR_NO_DEVICE: return ("No device"); case LIBUSB20_ERROR_NOT_FOUND: return ("Not found"); case LIBUSB20_ERROR_BUSY: return ("Device busy"); case LIBUSB20_ERROR_TIMEOUT: return ("Timeout"); case LIBUSB20_ERROR_OVERFLOW: return ("Overflow"); case LIBUSB20_ERROR_PIPE: return ("Pipe error"); case LIBUSB20_ERROR_INTERRUPTED: return ("Interrupted"); case LIBUSB20_ERROR_NO_MEM: return ("Out of memory"); case LIBUSB20_ERROR_NOT_SUPPORTED: return ("Not supported"); case LIBUSB20_ERROR_OTHER: return ("Other error"); default: return ("Unknown error"); } } const char * libusb20_error_name(int code) { switch (code) { case LIBUSB20_SUCCESS: return ("LIBUSB20_SUCCESS"); case LIBUSB20_ERROR_IO: return ("LIBUSB20_ERROR_IO"); case LIBUSB20_ERROR_INVALID_PARAM: return ("LIBUSB20_ERROR_INVALID_PARAM"); case LIBUSB20_ERROR_ACCESS: return ("LIBUSB20_ERROR_ACCESS"); case LIBUSB20_ERROR_NO_DEVICE: return ("LIBUSB20_ERROR_NO_DEVICE"); case LIBUSB20_ERROR_NOT_FOUND: return ("LIBUSB20_ERROR_NOT_FOUND"); case LIBUSB20_ERROR_BUSY: return ("LIBUSB20_ERROR_BUSY"); case LIBUSB20_ERROR_TIMEOUT: return ("LIBUSB20_ERROR_TIMEOUT"); case LIBUSB20_ERROR_OVERFLOW: return ("LIBUSB20_ERROR_OVERFLOW"); case LIBUSB20_ERROR_PIPE: return ("LIBUSB20_ERROR_PIPE"); case LIBUSB20_ERROR_INTERRUPTED: return ("LIBUSB20_ERROR_INTERRUPTED"); case LIBUSB20_ERROR_NO_MEM: return ("LIBUSB20_ERROR_NO_MEM"); case LIBUSB20_ERROR_NOT_SUPPORTED: return ("LIBUSB20_ERROR_NOT_SUPPORTED"); case LIBUSB20_ERROR_OTHER: return ("LIBUSB20_ERROR_OTHER"); default: return ("LIBUSB20_ERROR_UNKNOWN"); } } Index: stable/11/lib/libusb/libusb20_int.h =================================================================== --- stable/11/lib/libusb/libusb20_int.h (revision 331770) +++ stable/11/lib/libusb/libusb20_int.h (revision 331771) @@ -1,245 +1,246 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 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 * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ /* * This file describes internal structures. */ #ifndef _LIBUSB20_INT_H_ #define _LIBUSB20_INT_H_ #ifdef COMPAT_32BIT #define libusb20_pass_ptr(ptr) ((uint64_t)(uintptr_t)(ptr)) #else #define libusb20_pass_ptr(ptr) (ptr) #endif struct libusb20_device; struct libusb20_backend; struct libusb20_transfer; struct libusb20_quirk; union libusb20_session_data { unsigned long session_data; struct timespec tv; uint32_t plugtime; }; /* USB backend specific */ typedef const char *(libusb20_get_backend_name_t)(void); typedef int (libusb20_root_get_dev_quirk_t)(struct libusb20_backend *pbe, uint16_t index, struct libusb20_quirk *pq); typedef int (libusb20_root_get_quirk_name_t)(struct libusb20_backend *pbe, uint16_t index, struct libusb20_quirk *pq); typedef int (libusb20_root_add_dev_quirk_t)(struct libusb20_backend *pbe, struct libusb20_quirk *pq); typedef int (libusb20_root_remove_dev_quirk_t)(struct libusb20_backend *pbe, struct libusb20_quirk *pq); typedef int (libusb20_close_device_t)(struct libusb20_device *pdev); typedef int (libusb20_dev_get_info_t)(struct libusb20_device *pdev, struct usb_device_info *pinfo); typedef int (libusb20_dev_get_iface_desc_t)(struct libusb20_device *pdev, uint8_t iface_index, char *buf, uint8_t len); typedef int (libusb20_init_backend_t)(struct libusb20_backend *pbe); typedef int (libusb20_open_device_t)(struct libusb20_device *pdev, uint16_t transfer_count_max); typedef void (libusb20_exit_backend_t)(struct libusb20_backend *pbe); typedef int (libusb20_root_set_template_t)(struct libusb20_backend *pbe, int temp); typedef int (libusb20_root_get_template_t)(struct libusb20_backend *pbe, int *ptemp); #define LIBUSB20_DEFINE(n,field) \ libusb20_##field##_t *field; #define LIBUSB20_DECLARE(n,field) \ /* .field = */ n##_##field, #define LIBUSB20_BACKEND(m,n) \ /* description of this backend */ \ m(n, get_backend_name) \ /* optional backend methods */ \ m(n, init_backend) \ m(n, exit_backend) \ m(n, dev_get_info) \ m(n, dev_get_iface_desc) \ m(n, root_get_dev_quirk) \ m(n, root_get_quirk_name) \ m(n, root_add_dev_quirk) \ m(n, root_remove_dev_quirk) \ m(n, root_set_template) \ m(n, root_get_template) \ /* mandatory device methods */ \ m(n, open_device) \ m(n, close_device) \ struct libusb20_backend_methods { LIBUSB20_BACKEND(LIBUSB20_DEFINE,) }; /* USB dummy methods */ typedef int (libusb20_dummy_int_t)(void); typedef void (libusb20_dummy_void_t)(void); /* USB device specific */ typedef int (libusb20_detach_kernel_driver_t)(struct libusb20_device *pdev, uint8_t iface_index); typedef int (libusb20_do_request_sync_t)(struct libusb20_device *pdev, struct LIBUSB20_CONTROL_SETUP_DECODED *setup, void *data, uint16_t *pactlen, uint32_t timeout, uint8_t flags); typedef int (libusb20_get_config_desc_full_t)(struct libusb20_device *pdev, uint8_t **ppbuf, uint16_t *plen, uint8_t index); typedef int (libusb20_get_config_index_t)(struct libusb20_device *pdev, uint8_t *pindex); typedef int (libusb20_kernel_driver_active_t)(struct libusb20_device *pdev, uint8_t iface_index); typedef int (libusb20_process_t)(struct libusb20_device *pdev); typedef int (libusb20_reset_device_t)(struct libusb20_device *pdev); typedef int (libusb20_set_power_mode_t)(struct libusb20_device *pdev, uint8_t power_mode); typedef int (libusb20_get_power_mode_t)(struct libusb20_device *pdev, uint8_t *power_mode); -typedef int (libusb20_get_port_path_t)(struct libusb20_device *pdev, uint8_t *buf, uint8_t bufsize); typedef int (libusb20_get_power_usage_t)(struct libusb20_device *pdev, uint16_t *power_usage); typedef int (libusb20_set_alt_index_t)(struct libusb20_device *pdev, uint8_t iface_index, uint8_t alt_index); typedef int (libusb20_set_config_index_t)(struct libusb20_device *pdev, uint8_t index); typedef int (libusb20_check_connected_t)(struct libusb20_device *pdev); /* USB transfer specific */ typedef int (libusb20_tr_open_t)(struct libusb20_transfer *xfer, uint32_t MaxBufSize, uint32_t MaxFrameCount, uint8_t ep_no, uint16_t stream_id, uint8_t pre_scale); typedef int (libusb20_tr_close_t)(struct libusb20_transfer *xfer); typedef int (libusb20_tr_clear_stall_sync_t)(struct libusb20_transfer *xfer); typedef void (libusb20_tr_submit_t)(struct libusb20_transfer *xfer); typedef void (libusb20_tr_cancel_async_t)(struct libusb20_transfer *xfer); #define LIBUSB20_DEVICE(m,n) \ m(n, detach_kernel_driver) \ m(n, do_request_sync) \ m(n, get_config_desc_full) \ m(n, get_config_index) \ m(n, kernel_driver_active) \ m(n, process) \ m(n, reset_device) \ m(n, check_connected) \ m(n, set_power_mode) \ m(n, get_power_mode) \ - m(n, get_port_path) \ m(n, get_power_usage) \ m(n, set_alt_index) \ m(n, set_config_index) \ m(n, tr_cancel_async) \ m(n, tr_clear_stall_sync) \ m(n, tr_close) \ m(n, tr_open) \ m(n, tr_submit) \ struct libusb20_device_methods { LIBUSB20_DEVICE(LIBUSB20_DEFINE,) }; struct libusb20_backend { TAILQ_HEAD(, libusb20_device) usb_devs; const struct libusb20_backend_methods *methods; }; struct libusb20_transfer { struct libusb20_device *pdev; /* the USB device we belong to */ libusb20_tr_callback_t *callback; void *priv_sc0; /* private client data */ void *priv_sc1; /* private client data */ /* * Pointer to a list of buffer pointers: */ #ifdef COMPAT_32BIT uint64_t *ppBuffer; #else void **ppBuffer; #endif /* * Pointer to frame lengths, which are updated to actual length * after the USB transfer completes: */ uint32_t *pLength; uint32_t maxTotalLength; uint32_t maxFrames; /* total number of frames */ uint32_t nFrames; /* total number of frames */ uint32_t aFrames; /* actual number of frames */ uint32_t timeout; /* isochronous completion time in milliseconds */ uint16_t timeComplete; uint16_t trIndex; uint16_t maxPacketLen; uint8_t flags; /* see LIBUSB20_TRANSFER_XXX */ uint8_t status; /* see LIBUSB20_TRANSFER_XXX */ uint8_t is_opened; uint8_t is_pending; uint8_t is_cancel; uint8_t is_draining; uint8_t is_restart; }; struct libusb20_device { /* device descriptor */ struct LIBUSB20_DEVICE_DESC_DECODED ddesc; /* device timestamp */ union libusb20_session_data session_data; /* our device entry */ TAILQ_ENTRY(libusb20_device) dev_entry; /* device methods */ const struct libusb20_device_methods *methods; /* backend methods */ const struct libusb20_backend_methods *beMethods; /* list of USB transfers */ struct libusb20_transfer *pTransfer; /* private backend data */ void *privBeData; /* libUSB v0.1 and v1.0 compat data */ void *privLuData; /* claimed interface */ uint8_t claimed_interface; /* auto detach kernel driver */ uint8_t auto_detach; /* device file handle */ int file; /* device file handle (control transfers only) */ int file_ctrl; /* debugging level */ int debug; /* number of USB transfers */ uint16_t nTransfer; uint8_t bus_number; uint8_t device_address; uint8_t usb_mode; uint8_t usb_speed; uint8_t is_opened; uint8_t parent_address; uint8_t parent_port; + uint8_t port_level; char usb_desc[96]; +#define LIBUSB20_DEVICE_PORT_PATH_MAX 32 + uint8_t port_path[LIBUSB20_DEVICE_PORT_PATH_MAX]; }; extern const struct libusb20_backend_methods libusb20_ugen20_backend; extern const struct libusb20_backend_methods libusb20_linux_backend; #endif /* _LIBUSB20_INT_H_ */ Index: stable/11/lib/libusb/libusb20_ugen20.c =================================================================== --- stable/11/lib/libusb/libusb20_ugen20.c (revision 331770) +++ stable/11/lib/libusb/libusb20_ugen20.c (revision 331771) @@ -1,1070 +1,1061 @@ /* $FreeBSD$ */ /*- * Copyright (c) 2008 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 * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF * SUCH DAMAGE. */ #ifdef LIBUSB_GLOBAL_INCLUDE_FILE #include LIBUSB_GLOBAL_INCLUDE_FILE #else #include #include #include #include #include #include #include #include #include #endif #include #include #include #include "libusb20.h" #include "libusb20_desc.h" #include "libusb20_int.h" #ifndef IOUSB #define IOUSB(a) a #endif static libusb20_init_backend_t ugen20_init_backend; static libusb20_open_device_t ugen20_open_device; static libusb20_close_device_t ugen20_close_device; static libusb20_get_backend_name_t ugen20_get_backend_name; static libusb20_exit_backend_t ugen20_exit_backend; static libusb20_dev_get_iface_desc_t ugen20_dev_get_iface_desc; static libusb20_dev_get_info_t ugen20_dev_get_info; static libusb20_root_get_dev_quirk_t ugen20_root_get_dev_quirk; static libusb20_root_get_quirk_name_t ugen20_root_get_quirk_name; static libusb20_root_add_dev_quirk_t ugen20_root_add_dev_quirk; static libusb20_root_remove_dev_quirk_t ugen20_root_remove_dev_quirk; static libusb20_root_set_template_t ugen20_root_set_template; static libusb20_root_get_template_t ugen20_root_get_template; const struct libusb20_backend_methods libusb20_ugen20_backend = { LIBUSB20_BACKEND(LIBUSB20_DECLARE, ugen20) }; /* USB device specific */ static libusb20_get_config_desc_full_t ugen20_get_config_desc_full; static libusb20_get_config_index_t ugen20_get_config_index; static libusb20_set_config_index_t ugen20_set_config_index; static libusb20_set_alt_index_t ugen20_set_alt_index; static libusb20_reset_device_t ugen20_reset_device; static libusb20_check_connected_t ugen20_check_connected; static libusb20_set_power_mode_t ugen20_set_power_mode; static libusb20_get_power_mode_t ugen20_get_power_mode; -static libusb20_get_port_path_t ugen20_get_port_path; static libusb20_get_power_usage_t ugen20_get_power_usage; static libusb20_kernel_driver_active_t ugen20_kernel_driver_active; static libusb20_detach_kernel_driver_t ugen20_detach_kernel_driver; static libusb20_do_request_sync_t ugen20_do_request_sync; static libusb20_process_t ugen20_process; /* USB transfer specific */ static libusb20_tr_open_t ugen20_tr_open; static libusb20_tr_close_t ugen20_tr_close; static libusb20_tr_clear_stall_sync_t ugen20_tr_clear_stall_sync; static libusb20_tr_submit_t ugen20_tr_submit; static libusb20_tr_cancel_async_t ugen20_tr_cancel_async; static const struct libusb20_device_methods libusb20_ugen20_device_methods = { LIBUSB20_DEVICE(LIBUSB20_DECLARE, ugen20) }; static const char * ugen20_get_backend_name(void) { return ("FreeBSD UGEN 2.0"); } static uint32_t ugen20_path_convert_one(const char **pp) { const char *ptr; uint32_t temp = 0; ptr = *pp; while ((*ptr >= '0') && (*ptr <= '9')) { temp *= 10; temp += (*ptr - '0'); if (temp >= 1000000) { /* catch overflow early */ return (0xFFFFFFFF); } ptr++; } if (*ptr == '.') { /* skip dot */ ptr++; } *pp = ptr; return (temp); } static int ugen20_enumerate(struct libusb20_device *pdev, const char *id) { const char *tmp = id; struct usb_device_descriptor ddesc; struct usb_device_info devinfo; + struct usb_device_port_path udpp; uint32_t plugtime; char buf[64]; int f; int error; pdev->bus_number = ugen20_path_convert_one(&tmp); pdev->device_address = ugen20_path_convert_one(&tmp); snprintf(buf, sizeof(buf), "/dev/" USB_GENERIC_NAME "%u.%u", pdev->bus_number, pdev->device_address); f = open(buf, O_RDWR); if (f < 0) { return (LIBUSB20_ERROR_OTHER); } if (ioctl(f, IOUSB(USB_GET_PLUGTIME), &plugtime)) { error = LIBUSB20_ERROR_OTHER; goto done; } /* store when the device was plugged */ pdev->session_data.plugtime = plugtime; if (ioctl(f, IOUSB(USB_GET_DEVICE_DESC), &ddesc)) { error = LIBUSB20_ERROR_OTHER; goto done; } LIBUSB20_INIT(LIBUSB20_DEVICE_DESC, &(pdev->ddesc)); libusb20_me_decode(&ddesc, sizeof(ddesc), &(pdev->ddesc)); if (pdev->ddesc.bNumConfigurations == 0) { error = LIBUSB20_ERROR_OTHER; goto done; } else if (pdev->ddesc.bNumConfigurations >= 8) { error = LIBUSB20_ERROR_OTHER; goto done; } if (ioctl(f, IOUSB(USB_GET_DEVICEINFO), &devinfo)) { error = LIBUSB20_ERROR_OTHER; goto done; } switch (devinfo.udi_mode) { case USB_MODE_DEVICE: pdev->usb_mode = LIBUSB20_MODE_DEVICE; break; default: pdev->usb_mode = LIBUSB20_MODE_HOST; break; } switch (devinfo.udi_speed) { case USB_SPEED_LOW: pdev->usb_speed = LIBUSB20_SPEED_LOW; break; case USB_SPEED_FULL: pdev->usb_speed = LIBUSB20_SPEED_FULL; break; case USB_SPEED_HIGH: pdev->usb_speed = LIBUSB20_SPEED_HIGH; break; case USB_SPEED_VARIABLE: pdev->usb_speed = LIBUSB20_SPEED_VARIABLE; break; case USB_SPEED_SUPER: pdev->usb_speed = LIBUSB20_SPEED_SUPER; break; default: pdev->usb_speed = LIBUSB20_SPEED_UNKNOWN; break; } /* get parent HUB index and port */ pdev->parent_address = devinfo.udi_hubindex; pdev->parent_port = devinfo.udi_hubport; /* generate a nice description for printout */ snprintf(pdev->usb_desc, sizeof(pdev->usb_desc), USB_GENERIC_NAME "%u.%u: <%s %s> at usbus%u", pdev->bus_number, pdev->device_address, devinfo.udi_vendor, devinfo.udi_product, pdev->bus_number); + /* get device port path, if any */ + if (ioctl(f, IOUSB(USB_GET_DEV_PORT_PATH), &udpp) == 0 && + udpp.udp_port_level < LIBUSB20_DEVICE_PORT_PATH_MAX) { + memcpy(pdev->port_path, udpp.udp_port_no, udpp.udp_port_level); + pdev->port_level = udpp.udp_port_level; + } + error = 0; done: close(f); return (error); } struct ugen20_urd_state { struct usb_read_dir urd; uint32_t nparsed; int f; uint8_t *ptr; const char *src; const char *dst; uint8_t buf[256]; uint8_t dummy_zero[1]; }; static int ugen20_readdir(struct ugen20_urd_state *st) { ; /* style fix */ repeat: if (st->ptr == NULL) { st->urd.urd_startentry += st->nparsed; st->urd.urd_data = libusb20_pass_ptr(st->buf); st->urd.urd_maxlen = sizeof(st->buf); st->nparsed = 0; if (ioctl(st->f, IOUSB(USB_READ_DIR), &st->urd)) { return (EINVAL); } st->ptr = st->buf; } if (st->ptr[0] == 0) { if (st->nparsed) { st->ptr = NULL; goto repeat; } else { return (ENXIO); } } st->src = (void *)(st->ptr + 1); st->dst = st->src + strlen(st->src) + 1; st->ptr = st->ptr + st->ptr[0]; st->nparsed++; if ((st->ptr < st->buf) || (st->ptr > st->dummy_zero)) { /* invalid entry */ return (EINVAL); } return (0); } static int ugen20_init_backend(struct libusb20_backend *pbe) { struct ugen20_urd_state state; struct libusb20_device *pdev; memset(&state, 0, sizeof(state)); state.f = open("/dev/" USB_DEVICE_NAME, O_RDONLY); if (state.f < 0) return (LIBUSB20_ERROR_OTHER); while (ugen20_readdir(&state) == 0) { if ((state.src[0] != 'u') || (state.src[1] != 'g') || (state.src[2] != 'e') || (state.src[3] != 'n')) { continue; } pdev = libusb20_dev_alloc(); if (pdev == NULL) { continue; } if (ugen20_enumerate(pdev, state.src + 4)) { libusb20_dev_free(pdev); continue; } /* put the device on the backend list */ libusb20_be_enqueue_device(pbe, pdev); } close(state.f); return (0); /* success */ } static void ugen20_tr_release(struct libusb20_device *pdev) { struct usb_fs_uninit fs_uninit; if (pdev->nTransfer == 0) { return; } /* release all pending USB transfers */ if (pdev->privBeData != NULL) { memset(&fs_uninit, 0, sizeof(fs_uninit)); if (ioctl(pdev->file, IOUSB(USB_FS_UNINIT), &fs_uninit)) { /* ignore any errors of this kind */ } } return; } static int ugen20_tr_renew(struct libusb20_device *pdev) { struct usb_fs_init fs_init; struct usb_fs_endpoint *pfse; int error; uint32_t size; uint16_t nMaxTransfer; nMaxTransfer = pdev->nTransfer; error = 0; if (nMaxTransfer == 0) { goto done; } size = nMaxTransfer * sizeof(*pfse); if (pdev->privBeData == NULL) { pfse = malloc(size); if (pfse == NULL) { error = LIBUSB20_ERROR_NO_MEM; goto done; } pdev->privBeData = pfse; } /* reset endpoint data */ memset(pdev->privBeData, 0, size); memset(&fs_init, 0, sizeof(fs_init)); fs_init.pEndpoints = libusb20_pass_ptr(pdev->privBeData); fs_init.ep_index_max = nMaxTransfer; if (ioctl(pdev->file, IOUSB(USB_FS_INIT), &fs_init)) { error = LIBUSB20_ERROR_OTHER; goto done; } done: return (error); } static int ugen20_open_device(struct libusb20_device *pdev, uint16_t nMaxTransfer) { uint32_t plugtime; char buf[64]; int f; int g; int error; snprintf(buf, sizeof(buf), "/dev/" USB_GENERIC_NAME "%u.%u", pdev->bus_number, pdev->device_address); /* * We need two file handles, one for the control endpoint and one * for BULK, INTERRUPT and ISOCHRONOUS transactions due to optimised * kernel locking. */ g = open(buf, O_RDWR); if (g < 0) { return (LIBUSB20_ERROR_NO_DEVICE); } f = open(buf, O_RDWR); if (f < 0) { close(g); return (LIBUSB20_ERROR_NO_DEVICE); } if (ioctl(f, IOUSB(USB_GET_PLUGTIME), &plugtime)) { error = LIBUSB20_ERROR_OTHER; goto done; } /* check that the correct device is still plugged */ if (pdev->session_data.plugtime != plugtime) { error = LIBUSB20_ERROR_NO_DEVICE; goto done; } /* need to set this before "tr_renew()" */ pdev->file = f; pdev->file_ctrl = g; /* renew all USB transfers */ error = ugen20_tr_renew(pdev); if (error) { goto done; } /* set methods */ pdev->methods = &libusb20_ugen20_device_methods; done: if (error) { if (pdev->privBeData) { /* cleanup after "tr_renew()" */ free(pdev->privBeData); pdev->privBeData = NULL; } pdev->file = -1; pdev->file_ctrl = -1; close(f); close(g); } return (error); } static int ugen20_close_device(struct libusb20_device *pdev) { struct usb_fs_uninit fs_uninit; if (pdev->privBeData) { memset(&fs_uninit, 0, sizeof(fs_uninit)); if (ioctl(pdev->file, IOUSB(USB_FS_UNINIT), &fs_uninit)) { /* ignore this error */ } free(pdev->privBeData); } pdev->nTransfer = 0; pdev->privBeData = NULL; close(pdev->file); close(pdev->file_ctrl); pdev->file = -1; pdev->file_ctrl = -1; return (0); /* success */ } static void ugen20_exit_backend(struct libusb20_backend *pbe) { return; /* nothing to do */ } static int ugen20_get_config_desc_full(struct libusb20_device *pdev, uint8_t **ppbuf, uint16_t *plen, uint8_t cfg_index) { struct usb_gen_descriptor gen_desc; struct usb_config_descriptor cdesc; uint8_t *ptr; uint16_t len; int error; /* make sure memory is initialised */ memset(&cdesc, 0, sizeof(cdesc)); memset(&gen_desc, 0, sizeof(gen_desc)); gen_desc.ugd_data = libusb20_pass_ptr(&cdesc); gen_desc.ugd_maxlen = sizeof(cdesc); gen_desc.ugd_config_index = cfg_index; error = ioctl(pdev->file_ctrl, IOUSB(USB_GET_FULL_DESC), &gen_desc); if (error) { return (LIBUSB20_ERROR_OTHER); } len = UGETW(cdesc.wTotalLength); if (len < sizeof(cdesc)) { /* corrupt descriptor */ return (LIBUSB20_ERROR_OTHER); } ptr = malloc(len); if (!ptr) { return (LIBUSB20_ERROR_NO_MEM); } /* make sure memory is initialised */ memset(ptr, 0, len); gen_desc.ugd_data = libusb20_pass_ptr(ptr); gen_desc.ugd_maxlen = len; error = ioctl(pdev->file_ctrl, IOUSB(USB_GET_FULL_DESC), &gen_desc); if (error) { free(ptr); return (LIBUSB20_ERROR_OTHER); } /* make sure that the device doesn't fool us */ memcpy(ptr, &cdesc, sizeof(cdesc)); *ppbuf = ptr; *plen = len; return (0); /* success */ } static int ugen20_get_config_index(struct libusb20_device *pdev, uint8_t *pindex) { int temp; if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_CONFIG), &temp)) { return (LIBUSB20_ERROR_OTHER); } *pindex = temp; return (0); } static int ugen20_set_config_index(struct libusb20_device *pdev, uint8_t cfg_index) { int temp = cfg_index; /* release all active USB transfers */ ugen20_tr_release(pdev); if (ioctl(pdev->file_ctrl, IOUSB(USB_SET_CONFIG), &temp)) { return (LIBUSB20_ERROR_OTHER); } return (ugen20_tr_renew(pdev)); } static int ugen20_set_alt_index(struct libusb20_device *pdev, uint8_t iface_index, uint8_t alt_index) { struct usb_alt_interface alt_iface; memset(&alt_iface, 0, sizeof(alt_iface)); alt_iface.uai_interface_index = iface_index; alt_iface.uai_alt_index = alt_index; /* release all active USB transfers */ ugen20_tr_release(pdev); if (ioctl(pdev->file_ctrl, IOUSB(USB_SET_ALTINTERFACE), &alt_iface)) { return (LIBUSB20_ERROR_OTHER); } return (ugen20_tr_renew(pdev)); } static int ugen20_reset_device(struct libusb20_device *pdev) { int temp = 0; /* release all active USB transfers */ ugen20_tr_release(pdev); if (ioctl(pdev->file_ctrl, IOUSB(USB_DEVICEENUMERATE), &temp)) { return (LIBUSB20_ERROR_OTHER); } return (ugen20_tr_renew(pdev)); } static int ugen20_check_connected(struct libusb20_device *pdev) { uint32_t plugtime; int error = 0; if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_PLUGTIME), &plugtime)) { error = LIBUSB20_ERROR_NO_DEVICE; goto done; } if (pdev->session_data.plugtime != plugtime) { error = LIBUSB20_ERROR_NO_DEVICE; goto done; } done: return (error); } static int ugen20_set_power_mode(struct libusb20_device *pdev, uint8_t power_mode) { int temp; switch (power_mode) { case LIBUSB20_POWER_OFF: temp = USB_POWER_MODE_OFF; break; case LIBUSB20_POWER_ON: temp = USB_POWER_MODE_ON; break; case LIBUSB20_POWER_SAVE: temp = USB_POWER_MODE_SAVE; break; case LIBUSB20_POWER_SUSPEND: temp = USB_POWER_MODE_SUSPEND; break; case LIBUSB20_POWER_RESUME: temp = USB_POWER_MODE_RESUME; break; default: return (LIBUSB20_ERROR_INVALID_PARAM); } if (ioctl(pdev->file_ctrl, IOUSB(USB_SET_POWER_MODE), &temp)) { return (LIBUSB20_ERROR_OTHER); } return (0); } static int ugen20_get_power_mode(struct libusb20_device *pdev, uint8_t *power_mode) { int temp; if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_POWER_MODE), &temp)) { return (LIBUSB20_ERROR_OTHER); } switch (temp) { case USB_POWER_MODE_OFF: temp = LIBUSB20_POWER_OFF; break; case USB_POWER_MODE_ON: temp = LIBUSB20_POWER_ON; break; case USB_POWER_MODE_SAVE: temp = LIBUSB20_POWER_SAVE; break; case USB_POWER_MODE_SUSPEND: temp = LIBUSB20_POWER_SUSPEND; break; case USB_POWER_MODE_RESUME: temp = LIBUSB20_POWER_RESUME; break; default: temp = LIBUSB20_POWER_ON; break; } *power_mode = temp; return (0); /* success */ -} - -static int -ugen20_get_port_path(struct libusb20_device *pdev, uint8_t *buf, uint8_t bufsize) -{ - struct usb_device_port_path udpp; - - if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_DEV_PORT_PATH), &udpp)) - return (LIBUSB20_ERROR_OTHER); - - if (udpp.udp_port_level > bufsize) - return (LIBUSB20_ERROR_OVERFLOW); - - memcpy(buf, udpp.udp_port_no, udpp.udp_port_level); - - return (udpp.udp_port_level); /* success */ } static int ugen20_get_power_usage(struct libusb20_device *pdev, uint16_t *power_usage) { int temp; if (ioctl(pdev->file_ctrl, IOUSB(USB_GET_POWER_USAGE), &temp)) { return (LIBUSB20_ERROR_OTHER); } *power_usage = temp; return (0); /* success */ } static int ugen20_kernel_driver_active(struct libusb20_device *pdev, uint8_t iface_index) { int temp = iface_index; if (ioctl(pdev->file_ctrl, IOUSB(USB_IFACE_DRIVER_ACTIVE), &temp)) { return (LIBUSB20_ERROR_OTHER); } return (0); /* kernel driver is active */ } static int ugen20_detach_kernel_driver(struct libusb20_device *pdev, uint8_t iface_index) { int temp = iface_index; if (ioctl(pdev->file_ctrl, IOUSB(USB_IFACE_DRIVER_DETACH), &temp)) { return (LIBUSB20_ERROR_OTHER); } return (0); /* kernel driver is detached */ } static int ugen20_do_request_sync(struct libusb20_device *pdev, struct LIBUSB20_CONTROL_SETUP_DECODED *setup, void *data, uint16_t *pactlen, uint32_t timeout, uint8_t flags) { struct usb_ctl_request req; memset(&req, 0, sizeof(req)); req.ucr_data = libusb20_pass_ptr(data); if (!(flags & LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK)) { req.ucr_flags |= USB_SHORT_XFER_OK; } if (libusb20_me_encode(&req.ucr_request, sizeof(req.ucr_request), setup)) { /* ignore */ } if (ioctl(pdev->file_ctrl, IOUSB(USB_DO_REQUEST), &req)) { return (LIBUSB20_ERROR_OTHER); } if (pactlen) { /* get actual length */ *pactlen = req.ucr_actlen; } return (0); /* request was successful */ } static int ugen20_process(struct libusb20_device *pdev) { struct usb_fs_complete temp; struct usb_fs_endpoint *fsep; struct libusb20_transfer *xfer; while (1) { if (ioctl(pdev->file, IOUSB(USB_FS_COMPLETE), &temp)) { if (errno == EBUSY) { break; } else { /* device detached */ return (LIBUSB20_ERROR_OTHER); } } fsep = pdev->privBeData; xfer = pdev->pTransfer; fsep += temp.ep_index; xfer += temp.ep_index; /* update transfer status */ if (fsep->status == 0) { xfer->aFrames = fsep->aFrames; xfer->timeComplete = fsep->isoc_time_complete; xfer->status = LIBUSB20_TRANSFER_COMPLETED; } else if (fsep->status == USB_ERR_CANCELLED) { xfer->aFrames = 0; xfer->timeComplete = 0; xfer->status = LIBUSB20_TRANSFER_CANCELLED; } else if (fsep->status == USB_ERR_STALLED) { xfer->aFrames = 0; xfer->timeComplete = 0; xfer->status = LIBUSB20_TRANSFER_STALL; } else if (fsep->status == USB_ERR_TIMEOUT) { xfer->aFrames = 0; xfer->timeComplete = 0; xfer->status = LIBUSB20_TRANSFER_TIMED_OUT; } else { xfer->aFrames = 0; xfer->timeComplete = 0; xfer->status = LIBUSB20_TRANSFER_ERROR; } libusb20_tr_callback_wrapper(xfer); } return (0); /* done */ } static int ugen20_tr_open(struct libusb20_transfer *xfer, uint32_t MaxBufSize, uint32_t MaxFrameCount, uint8_t ep_no, uint16_t stream_id, uint8_t pre_scale) { union { struct usb_fs_open fs_open; struct usb_fs_open_stream fs_open_stream; } temp; struct usb_fs_endpoint *fsep; if (pre_scale) MaxFrameCount |= USB_FS_MAX_FRAMES_PRE_SCALE; memset(&temp, 0, sizeof(temp)); fsep = xfer->pdev->privBeData; fsep += xfer->trIndex; temp.fs_open.max_bufsize = MaxBufSize; temp.fs_open.max_frames = MaxFrameCount; temp.fs_open.ep_index = xfer->trIndex; temp.fs_open.ep_no = ep_no; if (stream_id != 0) { temp.fs_open_stream.stream_id = stream_id; if (ioctl(xfer->pdev->file, IOUSB(USB_FS_OPEN_STREAM), &temp.fs_open_stream)) return (LIBUSB20_ERROR_INVALID_PARAM); } else { if (ioctl(xfer->pdev->file, IOUSB(USB_FS_OPEN), &temp.fs_open)) return (LIBUSB20_ERROR_INVALID_PARAM); } /* maximums might have changed - update */ xfer->maxFrames = temp.fs_open.max_frames; /* "max_bufsize" should be multiple of "max_packet_length" */ xfer->maxTotalLength = temp.fs_open.max_bufsize; xfer->maxPacketLen = temp.fs_open.max_packet_length; /* setup buffer and length lists using zero copy */ fsep->ppBuffer = libusb20_pass_ptr(xfer->ppBuffer); fsep->pLength = libusb20_pass_ptr(xfer->pLength); return (0); /* success */ } static int ugen20_tr_close(struct libusb20_transfer *xfer) { struct usb_fs_close temp; memset(&temp, 0, sizeof(temp)); temp.ep_index = xfer->trIndex; if (ioctl(xfer->pdev->file, IOUSB(USB_FS_CLOSE), &temp)) { return (LIBUSB20_ERROR_INVALID_PARAM); } return (0); /* success */ } static int ugen20_tr_clear_stall_sync(struct libusb20_transfer *xfer) { struct usb_fs_clear_stall_sync temp; memset(&temp, 0, sizeof(temp)); /* if the transfer is active, an error will be returned */ temp.ep_index = xfer->trIndex; if (ioctl(xfer->pdev->file, IOUSB(USB_FS_CLEAR_STALL_SYNC), &temp)) { return (LIBUSB20_ERROR_INVALID_PARAM); } return (0); /* success */ } static void ugen20_tr_submit(struct libusb20_transfer *xfer) { struct usb_fs_start temp; struct usb_fs_endpoint *fsep; memset(&temp, 0, sizeof(temp)); fsep = xfer->pdev->privBeData; fsep += xfer->trIndex; fsep->nFrames = xfer->nFrames; fsep->flags = 0; if (!(xfer->flags & LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK)) { fsep->flags |= USB_FS_FLAG_SINGLE_SHORT_OK; } if (!(xfer->flags & LIBUSB20_TRANSFER_MULTI_SHORT_NOT_OK)) { fsep->flags |= USB_FS_FLAG_MULTI_SHORT_OK; } if (xfer->flags & LIBUSB20_TRANSFER_FORCE_SHORT) { fsep->flags |= USB_FS_FLAG_FORCE_SHORT; } if (xfer->flags & LIBUSB20_TRANSFER_DO_CLEAR_STALL) { fsep->flags |= USB_FS_FLAG_CLEAR_STALL; } /* NOTE: The "fsep->timeout" variable is 16-bit. */ if (xfer->timeout > 65535) fsep->timeout = 65535; else fsep->timeout = xfer->timeout; temp.ep_index = xfer->trIndex; if (ioctl(xfer->pdev->file, IOUSB(USB_FS_START), &temp)) { /* ignore any errors - should never happen */ } return; /* success */ } static void ugen20_tr_cancel_async(struct libusb20_transfer *xfer) { struct usb_fs_stop temp; memset(&temp, 0, sizeof(temp)); temp.ep_index = xfer->trIndex; if (ioctl(xfer->pdev->file, IOUSB(USB_FS_STOP), &temp)) { /* ignore any errors - should never happen */ } return; } static int ugen20_be_ioctl(uint32_t cmd, void *data) { int f; int error; f = open("/dev/" USB_DEVICE_NAME, O_RDONLY); if (f < 0) return (LIBUSB20_ERROR_OTHER); error = ioctl(f, cmd, data); if (error == -1) { if (errno == EPERM) { error = LIBUSB20_ERROR_ACCESS; } else { error = LIBUSB20_ERROR_OTHER; } } close(f); return (error); } static int ugen20_dev_get_iface_desc(struct libusb20_device *pdev, uint8_t iface_index, char *buf, uint8_t len) { struct usb_gen_descriptor ugd; memset(&ugd, 0, sizeof(ugd)); ugd.ugd_data = libusb20_pass_ptr(buf); ugd.ugd_maxlen = len; ugd.ugd_iface_index = iface_index; if (ioctl(pdev->file, IOUSB(USB_GET_IFACE_DRIVER), &ugd)) { return (LIBUSB20_ERROR_INVALID_PARAM); } return (0); } static int ugen20_dev_get_info(struct libusb20_device *pdev, struct usb_device_info *pinfo) { if (ioctl(pdev->file, IOUSB(USB_GET_DEVICEINFO), pinfo)) { return (LIBUSB20_ERROR_INVALID_PARAM); } return (0); } static int ugen20_root_get_dev_quirk(struct libusb20_backend *pbe, uint16_t quirk_index, struct libusb20_quirk *pq) { struct usb_gen_quirk q; int error; memset(&q, 0, sizeof(q)); q.index = quirk_index; error = ugen20_be_ioctl(IOUSB(USB_DEV_QUIRK_GET), &q); if (error) { if (errno == EINVAL) { return (LIBUSB20_ERROR_NOT_FOUND); } } else { pq->vid = q.vid; pq->pid = q.pid; pq->bcdDeviceLow = q.bcdDeviceLow; pq->bcdDeviceHigh = q.bcdDeviceHigh; strlcpy(pq->quirkname, q.quirkname, sizeof(pq->quirkname)); } return (error); } static int ugen20_root_get_quirk_name(struct libusb20_backend *pbe, uint16_t quirk_index, struct libusb20_quirk *pq) { struct usb_gen_quirk q; int error; memset(&q, 0, sizeof(q)); q.index = quirk_index; error = ugen20_be_ioctl(IOUSB(USB_QUIRK_NAME_GET), &q); if (error) { if (errno == EINVAL) { return (LIBUSB20_ERROR_NOT_FOUND); } } else { strlcpy(pq->quirkname, q.quirkname, sizeof(pq->quirkname)); } return (error); } static int ugen20_root_add_dev_quirk(struct libusb20_backend *pbe, struct libusb20_quirk *pq) { struct usb_gen_quirk q; int error; memset(&q, 0, sizeof(q)); q.vid = pq->vid; q.pid = pq->pid; q.bcdDeviceLow = pq->bcdDeviceLow; q.bcdDeviceHigh = pq->bcdDeviceHigh; strlcpy(q.quirkname, pq->quirkname, sizeof(q.quirkname)); error = ugen20_be_ioctl(IOUSB(USB_DEV_QUIRK_ADD), &q); if (error) { if (errno == ENOMEM) { return (LIBUSB20_ERROR_NO_MEM); } } return (error); } static int ugen20_root_remove_dev_quirk(struct libusb20_backend *pbe, struct libusb20_quirk *pq) { struct usb_gen_quirk q; int error; memset(&q, 0, sizeof(q)); q.vid = pq->vid; q.pid = pq->pid; q.bcdDeviceLow = pq->bcdDeviceLow; q.bcdDeviceHigh = pq->bcdDeviceHigh; strlcpy(q.quirkname, pq->quirkname, sizeof(q.quirkname)); error = ugen20_be_ioctl(IOUSB(USB_DEV_QUIRK_REMOVE), &q); if (error) { if (errno == EINVAL) { return (LIBUSB20_ERROR_NOT_FOUND); } } return (error); } static int ugen20_root_set_template(struct libusb20_backend *pbe, int temp) { return (ugen20_be_ioctl(IOUSB(USB_SET_TEMPLATE), &temp)); } static int ugen20_root_get_template(struct libusb20_backend *pbe, int *ptemp) { return (ugen20_be_ioctl(IOUSB(USB_GET_TEMPLATE), ptemp)); } Index: stable/11 =================================================================== --- stable/11 (revision 331770) +++ stable/11 (revision 331771) Property changes on: stable/11 ___________________________________________________________________ Modified: svn:mergeinfo ## -0,0 +0,1 ## Merged /head:r331419