1df4b8c2aSAndrew Thompson /* $FreeBSD$ */ 2df4b8c2aSAndrew Thompson /*- 35e53a4f9SPedro F. Giffuni * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 45e53a4f9SPedro F. Giffuni * 54594d907SAndrew Thompson * Copyright (c) 2008-2009 Hans Petter Selasky. All rights reserved. 6df4b8c2aSAndrew Thompson * 7df4b8c2aSAndrew Thompson * Redistribution and use in source and binary forms, with or without 8df4b8c2aSAndrew Thompson * modification, are permitted provided that the following conditions 9df4b8c2aSAndrew Thompson * are met: 10df4b8c2aSAndrew Thompson * 1. Redistributions of source code must retain the above copyright 11df4b8c2aSAndrew Thompson * notice, this list of conditions and the following disclaimer. 12df4b8c2aSAndrew Thompson * 2. Redistributions in binary form must reproduce the above copyright 13df4b8c2aSAndrew Thompson * notice, this list of conditions and the following disclaimer in the 14df4b8c2aSAndrew Thompson * documentation and/or other materials provided with the distribution. 15df4b8c2aSAndrew Thompson * 16df4b8c2aSAndrew Thompson * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17df4b8c2aSAndrew Thompson * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18df4b8c2aSAndrew Thompson * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19df4b8c2aSAndrew Thompson * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20df4b8c2aSAndrew Thompson * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21df4b8c2aSAndrew Thompson * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22df4b8c2aSAndrew Thompson * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23df4b8c2aSAndrew Thompson * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24df4b8c2aSAndrew Thompson * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25df4b8c2aSAndrew Thompson * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26df4b8c2aSAndrew Thompson * SUCH DAMAGE. 27df4b8c2aSAndrew Thompson */ 28df4b8c2aSAndrew Thompson 2966194130SHans Petter Selasky #ifdef LIBUSB_GLOBAL_INCLUDE_FILE 3066194130SHans Petter Selasky #include LIBUSB_GLOBAL_INCLUDE_FILE 3166194130SHans Petter Selasky #else 32f3cba95cSWojciech A. Koszek #include <ctype.h> 33f3cba95cSWojciech A. Koszek #include <poll.h> 34df4b8c2aSAndrew Thompson #include <stdio.h> 35df4b8c2aSAndrew Thompson #include <stdlib.h> 36df4b8c2aSAndrew Thompson #include <string.h> 3766194130SHans Petter Selasky #include <time.h> 3866194130SHans Petter Selasky #include <sys/queue.h> 3966194130SHans Petter Selasky #endif 40df4b8c2aSAndrew Thompson 41df4b8c2aSAndrew Thompson #include "libusb20.h" 42df4b8c2aSAndrew Thompson #include "libusb20_desc.h" 43df4b8c2aSAndrew Thompson #include "libusb20_int.h" 44df4b8c2aSAndrew Thompson 45df4b8c2aSAndrew Thompson static int 46df4b8c2aSAndrew Thompson dummy_int(void) 47df4b8c2aSAndrew Thompson { 48df4b8c2aSAndrew Thompson return (LIBUSB20_ERROR_NOT_SUPPORTED); 49df4b8c2aSAndrew Thompson } 50df4b8c2aSAndrew Thompson 51df4b8c2aSAndrew Thompson static void 52df4b8c2aSAndrew Thompson dummy_void(void) 53df4b8c2aSAndrew Thompson { 54df4b8c2aSAndrew Thompson return; 55df4b8c2aSAndrew Thompson } 56df4b8c2aSAndrew Thompson 57df4b8c2aSAndrew Thompson static void 58df4b8c2aSAndrew Thompson dummy_callback(struct libusb20_transfer *xfer) 59df4b8c2aSAndrew Thompson { 60df4b8c2aSAndrew Thompson ; /* style fix */ 61df4b8c2aSAndrew Thompson switch (libusb20_tr_get_status(xfer)) { 62df4b8c2aSAndrew Thompson case LIBUSB20_TRANSFER_START: 63df4b8c2aSAndrew Thompson libusb20_tr_submit(xfer); 64df4b8c2aSAndrew Thompson break; 65df4b8c2aSAndrew Thompson default: 66df4b8c2aSAndrew Thompson /* complete or error */ 67df4b8c2aSAndrew Thompson break; 68df4b8c2aSAndrew Thompson } 69df4b8c2aSAndrew Thompson return; 70df4b8c2aSAndrew Thompson } 71df4b8c2aSAndrew Thompson 72df4b8c2aSAndrew Thompson #define dummy_get_config_desc_full (void *)dummy_int 73df4b8c2aSAndrew Thompson #define dummy_get_config_index (void *)dummy_int 74df4b8c2aSAndrew Thompson #define dummy_set_config_index (void *)dummy_int 75df4b8c2aSAndrew Thompson #define dummy_set_alt_index (void *)dummy_int 76df4b8c2aSAndrew Thompson #define dummy_reset_device (void *)dummy_int 77e50ac68bSAndrew Thompson #define dummy_check_connected (void *)dummy_int 78df4b8c2aSAndrew Thompson #define dummy_set_power_mode (void *)dummy_int 79df4b8c2aSAndrew Thompson #define dummy_get_power_mode (void *)dummy_int 80aafcb732SHans Petter Selasky #define dummy_get_power_usage (void *)dummy_int 81*34b0ca24SHans Petter Selasky #define dummy_get_stats (void *)dummy_int 82df4b8c2aSAndrew Thompson #define dummy_kernel_driver_active (void *)dummy_int 83df4b8c2aSAndrew Thompson #define dummy_detach_kernel_driver (void *)dummy_int 84df4b8c2aSAndrew Thompson #define dummy_do_request_sync (void *)dummy_int 85df4b8c2aSAndrew Thompson #define dummy_tr_open (void *)dummy_int 86df4b8c2aSAndrew Thompson #define dummy_tr_close (void *)dummy_int 87df4b8c2aSAndrew Thompson #define dummy_tr_clear_stall_sync (void *)dummy_int 88df4b8c2aSAndrew Thompson #define dummy_process (void *)dummy_int 89df4b8c2aSAndrew Thompson #define dummy_dev_info (void *)dummy_int 90df4b8c2aSAndrew Thompson #define dummy_dev_get_iface_driver (void *)dummy_int 91df4b8c2aSAndrew Thompson 92df4b8c2aSAndrew Thompson #define dummy_tr_submit (void *)dummy_void 93df4b8c2aSAndrew Thompson #define dummy_tr_cancel_async (void *)dummy_void 94df4b8c2aSAndrew Thompson 95df4b8c2aSAndrew Thompson static const struct libusb20_device_methods libusb20_dummy_methods = { 96df4b8c2aSAndrew Thompson LIBUSB20_DEVICE(LIBUSB20_DECLARE, dummy) 97df4b8c2aSAndrew Thompson }; 98df4b8c2aSAndrew Thompson 99df4b8c2aSAndrew Thompson void 100df4b8c2aSAndrew Thompson libusb20_tr_callback_wrapper(struct libusb20_transfer *xfer) 101df4b8c2aSAndrew Thompson { 102df4b8c2aSAndrew Thompson ; /* style fix */ 103df4b8c2aSAndrew Thompson 104df4b8c2aSAndrew Thompson repeat: 105df4b8c2aSAndrew Thompson 106df4b8c2aSAndrew Thompson if (!xfer->is_pending) { 107df4b8c2aSAndrew Thompson xfer->status = LIBUSB20_TRANSFER_START; 108df4b8c2aSAndrew Thompson } else { 109df4b8c2aSAndrew Thompson xfer->is_pending = 0; 110df4b8c2aSAndrew Thompson } 111df4b8c2aSAndrew Thompson 112df4b8c2aSAndrew Thompson xfer->callback(xfer); 113df4b8c2aSAndrew Thompson 114df4b8c2aSAndrew Thompson if (xfer->is_restart) { 115df4b8c2aSAndrew Thompson xfer->is_restart = 0; 116df4b8c2aSAndrew Thompson goto repeat; 117df4b8c2aSAndrew Thompson } 118df4b8c2aSAndrew Thompson if (xfer->is_draining && 119df4b8c2aSAndrew Thompson (!xfer->is_pending)) { 120df4b8c2aSAndrew Thompson xfer->is_draining = 0; 121df4b8c2aSAndrew Thompson xfer->status = LIBUSB20_TRANSFER_DRAINED; 122df4b8c2aSAndrew Thompson xfer->callback(xfer); 123df4b8c2aSAndrew Thompson } 124df4b8c2aSAndrew Thompson return; 125df4b8c2aSAndrew Thompson } 126df4b8c2aSAndrew Thompson 127df4b8c2aSAndrew Thompson int 128df4b8c2aSAndrew Thompson libusb20_tr_close(struct libusb20_transfer *xfer) 129df4b8c2aSAndrew Thompson { 130df4b8c2aSAndrew Thompson int error; 131df4b8c2aSAndrew Thompson 132df4b8c2aSAndrew Thompson if (!xfer->is_opened) { 133df4b8c2aSAndrew Thompson return (LIBUSB20_ERROR_OTHER); 134df4b8c2aSAndrew Thompson } 135df4b8c2aSAndrew Thompson error = xfer->pdev->methods->tr_close(xfer); 136df4b8c2aSAndrew Thompson 137df4b8c2aSAndrew Thompson if (xfer->pLength) { 138df4b8c2aSAndrew Thompson free(xfer->pLength); 139df4b8c2aSAndrew Thompson } 140df4b8c2aSAndrew Thompson if (xfer->ppBuffer) { 141df4b8c2aSAndrew Thompson free(xfer->ppBuffer); 142df4b8c2aSAndrew Thompson } 143c740e8e4SAndrew Thompson /* reset variable fields in case the transfer is opened again */ 1442bf49386SPedro F. Giffuni xfer->priv_sc0 = NULL; 1452bf49386SPedro F. Giffuni xfer->priv_sc1 = NULL; 146df4b8c2aSAndrew Thompson xfer->is_opened = 0; 147c740e8e4SAndrew Thompson xfer->is_pending = 0; 148c740e8e4SAndrew Thompson xfer->is_cancel = 0; 149c740e8e4SAndrew Thompson xfer->is_draining = 0; 150c740e8e4SAndrew Thompson xfer->is_restart = 0; 151c740e8e4SAndrew Thompson xfer->status = 0; 152c740e8e4SAndrew Thompson xfer->flags = 0; 153c740e8e4SAndrew Thompson xfer->nFrames = 0; 154c740e8e4SAndrew Thompson xfer->aFrames = 0; 155c740e8e4SAndrew Thompson xfer->timeout = 0; 156df4b8c2aSAndrew Thompson xfer->maxFrames = 0; 157df4b8c2aSAndrew Thompson xfer->maxTotalLength = 0; 158df4b8c2aSAndrew Thompson xfer->maxPacketLen = 0; 159df4b8c2aSAndrew Thompson return (error); 160df4b8c2aSAndrew Thompson } 161df4b8c2aSAndrew Thompson 162df4b8c2aSAndrew Thompson int 163df4b8c2aSAndrew Thompson libusb20_tr_open(struct libusb20_transfer *xfer, uint32_t MaxBufSize, 164df4b8c2aSAndrew Thompson uint32_t MaxFrameCount, uint8_t ep_no) 165df4b8c2aSAndrew Thompson { 16607b6ce3bSHans Petter Selasky return (libusb20_tr_open_stream(xfer, MaxBufSize, MaxFrameCount, ep_no, 0)); 16707b6ce3bSHans Petter Selasky } 16807b6ce3bSHans Petter Selasky 16907b6ce3bSHans Petter Selasky int 17007b6ce3bSHans Petter Selasky libusb20_tr_open_stream(struct libusb20_transfer *xfer, uint32_t MaxBufSize, 17107b6ce3bSHans Petter Selasky uint32_t MaxFrameCount, uint8_t ep_no, uint16_t stream_id) 17207b6ce3bSHans Petter Selasky { 173df4b8c2aSAndrew Thompson uint32_t size; 1741c497368SHans Petter Selasky uint8_t pre_scale; 175df4b8c2aSAndrew Thompson int error; 176df4b8c2aSAndrew Thompson 1771c497368SHans Petter Selasky if (xfer->is_opened) 178df4b8c2aSAndrew Thompson return (LIBUSB20_ERROR_BUSY); 1791c497368SHans Petter Selasky if (MaxFrameCount & LIBUSB20_MAX_FRAME_PRE_SCALE) { 1801c497368SHans Petter Selasky MaxFrameCount &= ~LIBUSB20_MAX_FRAME_PRE_SCALE; 1815f518148SHans Petter Selasky /* 1825f518148SHans Petter Selasky * The kernel can setup 8 times more frames when 1835f518148SHans Petter Selasky * pre-scaling ISOCHRONOUS transfers. Make sure the 1845f518148SHans Petter Selasky * length and pointer buffers are big enough: 1855f518148SHans Petter Selasky */ 1865f518148SHans Petter Selasky MaxFrameCount *= 8; 1871c497368SHans Petter Selasky pre_scale = 1; 1881c497368SHans Petter Selasky } else { 1891c497368SHans Petter Selasky pre_scale = 0; 190df4b8c2aSAndrew Thompson } 1911c497368SHans Petter Selasky if (MaxFrameCount == 0) 192df4b8c2aSAndrew Thompson return (LIBUSB20_ERROR_INVALID_PARAM); 1931c497368SHans Petter Selasky 194df4b8c2aSAndrew Thompson xfer->maxFrames = MaxFrameCount; 195df4b8c2aSAndrew Thompson 196df4b8c2aSAndrew Thompson size = MaxFrameCount * sizeof(xfer->pLength[0]); 197df4b8c2aSAndrew Thompson xfer->pLength = malloc(size); 198df4b8c2aSAndrew Thompson if (xfer->pLength == NULL) { 199df4b8c2aSAndrew Thompson return (LIBUSB20_ERROR_NO_MEM); 200df4b8c2aSAndrew Thompson } 201df4b8c2aSAndrew Thompson memset(xfer->pLength, 0, size); 202df4b8c2aSAndrew Thompson 203df4b8c2aSAndrew Thompson size = MaxFrameCount * sizeof(xfer->ppBuffer[0]); 204df4b8c2aSAndrew Thompson xfer->ppBuffer = malloc(size); 205df4b8c2aSAndrew Thompson if (xfer->ppBuffer == NULL) { 206df4b8c2aSAndrew Thompson free(xfer->pLength); 207df4b8c2aSAndrew Thompson return (LIBUSB20_ERROR_NO_MEM); 208df4b8c2aSAndrew Thompson } 209df4b8c2aSAndrew Thompson memset(xfer->ppBuffer, 0, size); 210df4b8c2aSAndrew Thompson 2115f518148SHans Petter Selasky if (pre_scale) { 212df4b8c2aSAndrew Thompson error = xfer->pdev->methods->tr_open(xfer, MaxBufSize, 2135f518148SHans Petter Selasky MaxFrameCount / 8, ep_no, stream_id, 1); 2145f518148SHans Petter Selasky } else { 2155f518148SHans Petter Selasky error = xfer->pdev->methods->tr_open(xfer, MaxBufSize, 2165f518148SHans Petter Selasky MaxFrameCount, ep_no, stream_id, 0); 2175f518148SHans Petter Selasky } 218df4b8c2aSAndrew Thompson 219df4b8c2aSAndrew Thompson if (error) { 220df4b8c2aSAndrew Thompson free(xfer->ppBuffer); 221df4b8c2aSAndrew Thompson free(xfer->pLength); 222df4b8c2aSAndrew Thompson } else { 223df4b8c2aSAndrew Thompson xfer->is_opened = 1; 224df4b8c2aSAndrew Thompson } 225df4b8c2aSAndrew Thompson return (error); 226df4b8c2aSAndrew Thompson } 227df4b8c2aSAndrew Thompson 228df4b8c2aSAndrew Thompson struct libusb20_transfer * 229df4b8c2aSAndrew Thompson libusb20_tr_get_pointer(struct libusb20_device *pdev, uint16_t trIndex) 230df4b8c2aSAndrew Thompson { 231df4b8c2aSAndrew Thompson if (trIndex >= pdev->nTransfer) { 232df4b8c2aSAndrew Thompson return (NULL); 233df4b8c2aSAndrew Thompson } 234df4b8c2aSAndrew Thompson return (pdev->pTransfer + trIndex); 235df4b8c2aSAndrew Thompson } 236df4b8c2aSAndrew Thompson 237df4b8c2aSAndrew Thompson uint32_t 238df4b8c2aSAndrew Thompson libusb20_tr_get_actual_frames(struct libusb20_transfer *xfer) 239df4b8c2aSAndrew Thompson { 240df4b8c2aSAndrew Thompson return (xfer->aFrames); 241df4b8c2aSAndrew Thompson } 242df4b8c2aSAndrew Thompson 243df4b8c2aSAndrew Thompson uint16_t 244df4b8c2aSAndrew Thompson libusb20_tr_get_time_complete(struct libusb20_transfer *xfer) 245df4b8c2aSAndrew Thompson { 246df4b8c2aSAndrew Thompson return (xfer->timeComplete); 247df4b8c2aSAndrew Thompson } 248df4b8c2aSAndrew Thompson 249df4b8c2aSAndrew Thompson uint32_t 250df4b8c2aSAndrew Thompson libusb20_tr_get_actual_length(struct libusb20_transfer *xfer) 251df4b8c2aSAndrew Thompson { 252df4b8c2aSAndrew Thompson uint32_t x; 253df4b8c2aSAndrew Thompson uint32_t actlen = 0; 254df4b8c2aSAndrew Thompson 255df4b8c2aSAndrew Thompson for (x = 0; x != xfer->aFrames; x++) { 256df4b8c2aSAndrew Thompson actlen += xfer->pLength[x]; 257df4b8c2aSAndrew Thompson } 258df4b8c2aSAndrew Thompson return (actlen); 259df4b8c2aSAndrew Thompson } 260df4b8c2aSAndrew Thompson 261df4b8c2aSAndrew Thompson uint32_t 262df4b8c2aSAndrew Thompson libusb20_tr_get_max_frames(struct libusb20_transfer *xfer) 263df4b8c2aSAndrew Thompson { 264df4b8c2aSAndrew Thompson return (xfer->maxFrames); 265df4b8c2aSAndrew Thompson } 266df4b8c2aSAndrew Thompson 267df4b8c2aSAndrew Thompson uint32_t 268df4b8c2aSAndrew Thompson libusb20_tr_get_max_packet_length(struct libusb20_transfer *xfer) 269df4b8c2aSAndrew Thompson { 270df4b8c2aSAndrew Thompson /* 271df4b8c2aSAndrew Thompson * Special Case NOTE: If the packet multiplier is non-zero for 272df4b8c2aSAndrew Thompson * High Speed USB, the value returned is equal to 273df4b8c2aSAndrew Thompson * "wMaxPacketSize * multiplier" ! 274df4b8c2aSAndrew Thompson */ 275df4b8c2aSAndrew Thompson return (xfer->maxPacketLen); 276df4b8c2aSAndrew Thompson } 277df4b8c2aSAndrew Thompson 278df4b8c2aSAndrew Thompson uint32_t 279df4b8c2aSAndrew Thompson libusb20_tr_get_max_total_length(struct libusb20_transfer *xfer) 280df4b8c2aSAndrew Thompson { 281df4b8c2aSAndrew Thompson return (xfer->maxTotalLength); 282df4b8c2aSAndrew Thompson } 283df4b8c2aSAndrew Thompson 284df4b8c2aSAndrew Thompson uint8_t 285df4b8c2aSAndrew Thompson libusb20_tr_get_status(struct libusb20_transfer *xfer) 286df4b8c2aSAndrew Thompson { 287df4b8c2aSAndrew Thompson return (xfer->status); 288df4b8c2aSAndrew Thompson } 289df4b8c2aSAndrew Thompson 290df4b8c2aSAndrew Thompson uint8_t 291df4b8c2aSAndrew Thompson libusb20_tr_pending(struct libusb20_transfer *xfer) 292df4b8c2aSAndrew Thompson { 293df4b8c2aSAndrew Thompson return (xfer->is_pending); 294df4b8c2aSAndrew Thompson } 295df4b8c2aSAndrew Thompson 296df4b8c2aSAndrew Thompson void * 297df4b8c2aSAndrew Thompson libusb20_tr_get_priv_sc0(struct libusb20_transfer *xfer) 298df4b8c2aSAndrew Thompson { 299df4b8c2aSAndrew Thompson return (xfer->priv_sc0); 300df4b8c2aSAndrew Thompson } 301df4b8c2aSAndrew Thompson 302df4b8c2aSAndrew Thompson void * 303df4b8c2aSAndrew Thompson libusb20_tr_get_priv_sc1(struct libusb20_transfer *xfer) 304df4b8c2aSAndrew Thompson { 305df4b8c2aSAndrew Thompson return (xfer->priv_sc1); 306df4b8c2aSAndrew Thompson } 307df4b8c2aSAndrew Thompson 308df4b8c2aSAndrew Thompson void 309df4b8c2aSAndrew Thompson libusb20_tr_stop(struct libusb20_transfer *xfer) 310df4b8c2aSAndrew Thompson { 3114594d907SAndrew Thompson if (!xfer->is_opened) { 3124594d907SAndrew Thompson /* transfer is not opened */ 3134594d907SAndrew Thompson return; 3144594d907SAndrew Thompson } 315df4b8c2aSAndrew Thompson if (!xfer->is_pending) { 316df4b8c2aSAndrew Thompson /* transfer not pending */ 317df4b8c2aSAndrew Thompson return; 318df4b8c2aSAndrew Thompson } 319df4b8c2aSAndrew Thompson if (xfer->is_cancel) { 320df4b8c2aSAndrew Thompson /* already cancelling */ 321df4b8c2aSAndrew Thompson return; 322df4b8c2aSAndrew Thompson } 323df4b8c2aSAndrew Thompson xfer->is_cancel = 1; /* we are cancelling */ 324df4b8c2aSAndrew Thompson 325df4b8c2aSAndrew Thompson xfer->pdev->methods->tr_cancel_async(xfer); 326df4b8c2aSAndrew Thompson return; 327df4b8c2aSAndrew Thompson } 328df4b8c2aSAndrew Thompson 329df4b8c2aSAndrew Thompson void 330df4b8c2aSAndrew Thompson libusb20_tr_drain(struct libusb20_transfer *xfer) 331df4b8c2aSAndrew Thompson { 3324594d907SAndrew Thompson if (!xfer->is_opened) { 3334594d907SAndrew Thompson /* transfer is not opened */ 3344594d907SAndrew Thompson return; 3354594d907SAndrew Thompson } 336df4b8c2aSAndrew Thompson /* make sure that we are cancelling */ 337df4b8c2aSAndrew Thompson libusb20_tr_stop(xfer); 338df4b8c2aSAndrew Thompson 339df4b8c2aSAndrew Thompson if (xfer->is_pending) { 340df4b8c2aSAndrew Thompson xfer->is_draining = 1; 341df4b8c2aSAndrew Thompson } 342df4b8c2aSAndrew Thompson return; 343df4b8c2aSAndrew Thompson } 344df4b8c2aSAndrew Thompson 345df4b8c2aSAndrew Thompson void 346df4b8c2aSAndrew Thompson libusb20_tr_clear_stall_sync(struct libusb20_transfer *xfer) 347df4b8c2aSAndrew Thompson { 348df4b8c2aSAndrew Thompson xfer->pdev->methods->tr_clear_stall_sync(xfer); 349df4b8c2aSAndrew Thompson return; 350df4b8c2aSAndrew Thompson } 351df4b8c2aSAndrew Thompson 352df4b8c2aSAndrew Thompson void 353df4b8c2aSAndrew Thompson libusb20_tr_set_buffer(struct libusb20_transfer *xfer, void *buffer, uint16_t frIndex) 354df4b8c2aSAndrew Thompson { 35551fd3d75SHans Petter Selasky xfer->ppBuffer[frIndex] = libusb20_pass_ptr(buffer); 356df4b8c2aSAndrew Thompson return; 357df4b8c2aSAndrew Thompson } 358df4b8c2aSAndrew Thompson 359df4b8c2aSAndrew Thompson void 360df4b8c2aSAndrew Thompson libusb20_tr_set_callback(struct libusb20_transfer *xfer, libusb20_tr_callback_t *cb) 361df4b8c2aSAndrew Thompson { 362df4b8c2aSAndrew Thompson xfer->callback = cb; 363df4b8c2aSAndrew Thompson return; 364df4b8c2aSAndrew Thompson } 365df4b8c2aSAndrew Thompson 366df4b8c2aSAndrew Thompson void 367df4b8c2aSAndrew Thompson libusb20_tr_set_flags(struct libusb20_transfer *xfer, uint8_t flags) 368df4b8c2aSAndrew Thompson { 369df4b8c2aSAndrew Thompson xfer->flags = flags; 370df4b8c2aSAndrew Thompson return; 371df4b8c2aSAndrew Thompson } 372df4b8c2aSAndrew Thompson 373545b01adSAndrew Thompson uint32_t 374545b01adSAndrew Thompson libusb20_tr_get_length(struct libusb20_transfer *xfer, uint16_t frIndex) 375545b01adSAndrew Thompson { 376545b01adSAndrew Thompson return (xfer->pLength[frIndex]); 377545b01adSAndrew Thompson } 378545b01adSAndrew Thompson 379df4b8c2aSAndrew Thompson void 380df4b8c2aSAndrew Thompson libusb20_tr_set_length(struct libusb20_transfer *xfer, uint32_t length, uint16_t frIndex) 381df4b8c2aSAndrew Thompson { 382df4b8c2aSAndrew Thompson xfer->pLength[frIndex] = length; 383df4b8c2aSAndrew Thompson return; 384df4b8c2aSAndrew Thompson } 385df4b8c2aSAndrew Thompson 386df4b8c2aSAndrew Thompson void 387df4b8c2aSAndrew Thompson libusb20_tr_set_priv_sc0(struct libusb20_transfer *xfer, void *sc0) 388df4b8c2aSAndrew Thompson { 389df4b8c2aSAndrew Thompson xfer->priv_sc0 = sc0; 390df4b8c2aSAndrew Thompson return; 391df4b8c2aSAndrew Thompson } 392df4b8c2aSAndrew Thompson 393df4b8c2aSAndrew Thompson void 394df4b8c2aSAndrew Thompson libusb20_tr_set_priv_sc1(struct libusb20_transfer *xfer, void *sc1) 395df4b8c2aSAndrew Thompson { 396df4b8c2aSAndrew Thompson xfer->priv_sc1 = sc1; 397df4b8c2aSAndrew Thompson return; 398df4b8c2aSAndrew Thompson } 399df4b8c2aSAndrew Thompson 400df4b8c2aSAndrew Thompson void 401df4b8c2aSAndrew Thompson libusb20_tr_set_timeout(struct libusb20_transfer *xfer, uint32_t timeout) 402df4b8c2aSAndrew Thompson { 403df4b8c2aSAndrew Thompson xfer->timeout = timeout; 404df4b8c2aSAndrew Thompson return; 405df4b8c2aSAndrew Thompson } 406df4b8c2aSAndrew Thompson 407df4b8c2aSAndrew Thompson void 408df4b8c2aSAndrew Thompson libusb20_tr_set_total_frames(struct libusb20_transfer *xfer, uint32_t nFrames) 409df4b8c2aSAndrew Thompson { 410df4b8c2aSAndrew Thompson if (nFrames > xfer->maxFrames) { 411df4b8c2aSAndrew Thompson /* should not happen */ 412df4b8c2aSAndrew Thompson nFrames = xfer->maxFrames; 413df4b8c2aSAndrew Thompson } 414df4b8c2aSAndrew Thompson xfer->nFrames = nFrames; 415df4b8c2aSAndrew Thompson return; 416df4b8c2aSAndrew Thompson } 417df4b8c2aSAndrew Thompson 418df4b8c2aSAndrew Thompson void 419df4b8c2aSAndrew Thompson libusb20_tr_setup_bulk(struct libusb20_transfer *xfer, void *pBuf, uint32_t length, uint32_t timeout) 420df4b8c2aSAndrew Thompson { 42151fd3d75SHans Petter Selasky xfer->ppBuffer[0] = libusb20_pass_ptr(pBuf); 422df4b8c2aSAndrew Thompson xfer->pLength[0] = length; 423df4b8c2aSAndrew Thompson xfer->timeout = timeout; 424df4b8c2aSAndrew Thompson xfer->nFrames = 1; 425df4b8c2aSAndrew Thompson return; 426df4b8c2aSAndrew Thompson } 427df4b8c2aSAndrew Thompson 428df4b8c2aSAndrew Thompson void 429df4b8c2aSAndrew Thompson libusb20_tr_setup_control(struct libusb20_transfer *xfer, void *psetup, void *pBuf, uint32_t timeout) 430df4b8c2aSAndrew Thompson { 431df4b8c2aSAndrew Thompson uint16_t len; 432df4b8c2aSAndrew Thompson 43351fd3d75SHans Petter Selasky xfer->ppBuffer[0] = libusb20_pass_ptr(psetup); 434df4b8c2aSAndrew Thompson xfer->pLength[0] = 8; /* fixed */ 435df4b8c2aSAndrew Thompson xfer->timeout = timeout; 436df4b8c2aSAndrew Thompson 437df4b8c2aSAndrew Thompson len = ((uint8_t *)psetup)[6] | (((uint8_t *)psetup)[7] << 8); 438df4b8c2aSAndrew Thompson 439df4b8c2aSAndrew Thompson if (len != 0) { 440df4b8c2aSAndrew Thompson xfer->nFrames = 2; 44151fd3d75SHans Petter Selasky xfer->ppBuffer[1] = libusb20_pass_ptr(pBuf); 442df4b8c2aSAndrew Thompson xfer->pLength[1] = len; 443df4b8c2aSAndrew Thompson } else { 444df4b8c2aSAndrew Thompson xfer->nFrames = 1; 445df4b8c2aSAndrew Thompson } 446df4b8c2aSAndrew Thompson return; 447df4b8c2aSAndrew Thompson } 448df4b8c2aSAndrew Thompson 449df4b8c2aSAndrew Thompson void 450df4b8c2aSAndrew Thompson libusb20_tr_setup_intr(struct libusb20_transfer *xfer, void *pBuf, uint32_t length, uint32_t timeout) 451df4b8c2aSAndrew Thompson { 45251fd3d75SHans Petter Selasky xfer->ppBuffer[0] = libusb20_pass_ptr(pBuf); 453df4b8c2aSAndrew Thompson xfer->pLength[0] = length; 454df4b8c2aSAndrew Thompson xfer->timeout = timeout; 455df4b8c2aSAndrew Thompson xfer->nFrames = 1; 456df4b8c2aSAndrew Thompson return; 457df4b8c2aSAndrew Thompson } 458df4b8c2aSAndrew Thompson 459df4b8c2aSAndrew Thompson void 460df4b8c2aSAndrew Thompson libusb20_tr_setup_isoc(struct libusb20_transfer *xfer, void *pBuf, uint32_t length, uint16_t frIndex) 461df4b8c2aSAndrew Thompson { 462df4b8c2aSAndrew Thompson if (frIndex >= xfer->maxFrames) { 463df4b8c2aSAndrew Thompson /* should not happen */ 464df4b8c2aSAndrew Thompson return; 465df4b8c2aSAndrew Thompson } 46651fd3d75SHans Petter Selasky xfer->ppBuffer[frIndex] = libusb20_pass_ptr(pBuf); 467df4b8c2aSAndrew Thompson xfer->pLength[frIndex] = length; 468df4b8c2aSAndrew Thompson return; 469df4b8c2aSAndrew Thompson } 470df4b8c2aSAndrew Thompson 4714594d907SAndrew Thompson uint8_t 4724594d907SAndrew Thompson libusb20_tr_bulk_intr_sync(struct libusb20_transfer *xfer, 4734594d907SAndrew Thompson void *pbuf, uint32_t length, uint32_t *pactlen, 4744594d907SAndrew Thompson uint32_t timeout) 4754594d907SAndrew Thompson { 4764594d907SAndrew Thompson struct libusb20_device *pdev = xfer->pdev; 4774594d907SAndrew Thompson uint32_t transfer_max; 4784594d907SAndrew Thompson uint32_t transfer_act; 4794594d907SAndrew Thompson uint8_t retval; 4804594d907SAndrew Thompson 4814594d907SAndrew Thompson /* set some sensible default value */ 4824594d907SAndrew Thompson if (pactlen != NULL) 4834594d907SAndrew Thompson *pactlen = 0; 4844594d907SAndrew Thompson 4854594d907SAndrew Thompson /* check for error condition */ 4864594d907SAndrew Thompson if (libusb20_tr_pending(xfer)) 4874594d907SAndrew Thompson return (LIBUSB20_ERROR_OTHER); 4884594d907SAndrew Thompson 4894594d907SAndrew Thompson do { 4904594d907SAndrew Thompson /* compute maximum transfer length */ 4914594d907SAndrew Thompson transfer_max = 4924594d907SAndrew Thompson libusb20_tr_get_max_total_length(xfer); 4934594d907SAndrew Thompson 4944594d907SAndrew Thompson if (transfer_max > length) 4954594d907SAndrew Thompson transfer_max = length; 4964594d907SAndrew Thompson 4974594d907SAndrew Thompson /* setup bulk or interrupt transfer */ 4984594d907SAndrew Thompson libusb20_tr_setup_bulk(xfer, pbuf, 4994594d907SAndrew Thompson transfer_max, timeout); 5004594d907SAndrew Thompson 5014594d907SAndrew Thompson /* start the transfer */ 5024594d907SAndrew Thompson libusb20_tr_start(xfer); 5034594d907SAndrew Thompson 5044594d907SAndrew Thompson /* wait for transfer completion */ 5054594d907SAndrew Thompson while (libusb20_dev_process(pdev) == 0) { 5064594d907SAndrew Thompson 5074594d907SAndrew Thompson if (libusb20_tr_pending(xfer) == 0) 5084594d907SAndrew Thompson break; 5094594d907SAndrew Thompson 5104594d907SAndrew Thompson libusb20_dev_wait_process(pdev, -1); 5114594d907SAndrew Thompson } 5124594d907SAndrew Thompson 5134594d907SAndrew Thompson transfer_act = libusb20_tr_get_actual_length(xfer); 5144594d907SAndrew Thompson 5154594d907SAndrew Thompson /* update actual length, if any */ 5164594d907SAndrew Thompson if (pactlen != NULL) 5174594d907SAndrew Thompson pactlen[0] += transfer_act; 5184594d907SAndrew Thompson 5194594d907SAndrew Thompson /* check transfer status */ 5204594d907SAndrew Thompson retval = libusb20_tr_get_status(xfer); 5214594d907SAndrew Thompson if (retval) 5224594d907SAndrew Thompson break; 5234594d907SAndrew Thompson 5244594d907SAndrew Thompson /* check for short transfer */ 5254594d907SAndrew Thompson if (transfer_act != transfer_max) 5264594d907SAndrew Thompson break; 5274594d907SAndrew Thompson 5284594d907SAndrew Thompson /* update buffer pointer and length */ 5294594d907SAndrew Thompson pbuf = ((uint8_t *)pbuf) + transfer_max; 5304594d907SAndrew Thompson length = length - transfer_max; 5314594d907SAndrew Thompson 5324594d907SAndrew Thompson } while (length != 0); 5334594d907SAndrew Thompson 5344594d907SAndrew Thompson return (retval); 5354594d907SAndrew Thompson } 5364594d907SAndrew Thompson 537df4b8c2aSAndrew Thompson void 538df4b8c2aSAndrew Thompson libusb20_tr_submit(struct libusb20_transfer *xfer) 539df4b8c2aSAndrew Thompson { 5404594d907SAndrew Thompson if (!xfer->is_opened) { 5414594d907SAndrew Thompson /* transfer is not opened */ 5424594d907SAndrew Thompson return; 5434594d907SAndrew Thompson } 544df4b8c2aSAndrew Thompson if (xfer->is_pending) { 545df4b8c2aSAndrew Thompson /* should not happen */ 546df4b8c2aSAndrew Thompson return; 547df4b8c2aSAndrew Thompson } 548df4b8c2aSAndrew Thompson xfer->is_pending = 1; /* we are pending */ 549df4b8c2aSAndrew Thompson xfer->is_cancel = 0; /* not cancelling */ 550df4b8c2aSAndrew Thompson xfer->is_restart = 0; /* not restarting */ 551df4b8c2aSAndrew Thompson 552df4b8c2aSAndrew Thompson xfer->pdev->methods->tr_submit(xfer); 553df4b8c2aSAndrew Thompson return; 554df4b8c2aSAndrew Thompson } 555df4b8c2aSAndrew Thompson 556df4b8c2aSAndrew Thompson void 557df4b8c2aSAndrew Thompson libusb20_tr_start(struct libusb20_transfer *xfer) 558df4b8c2aSAndrew Thompson { 5594594d907SAndrew Thompson if (!xfer->is_opened) { 5604594d907SAndrew Thompson /* transfer is not opened */ 5614594d907SAndrew Thompson return; 5624594d907SAndrew Thompson } 563df4b8c2aSAndrew Thompson if (xfer->is_pending) { 564df4b8c2aSAndrew Thompson if (xfer->is_cancel) { 565df4b8c2aSAndrew Thompson /* cancelling - restart */ 566df4b8c2aSAndrew Thompson xfer->is_restart = 1; 567df4b8c2aSAndrew Thompson } 568df4b8c2aSAndrew Thompson /* transfer not pending */ 569df4b8c2aSAndrew Thompson return; 570df4b8c2aSAndrew Thompson } 571df4b8c2aSAndrew Thompson /* get into the callback */ 572df4b8c2aSAndrew Thompson libusb20_tr_callback_wrapper(xfer); 573df4b8c2aSAndrew Thompson return; 574df4b8c2aSAndrew Thompson } 575df4b8c2aSAndrew Thompson 576df4b8c2aSAndrew Thompson /* USB device operations */ 577df4b8c2aSAndrew Thompson 578df4b8c2aSAndrew Thompson int 579df4b8c2aSAndrew Thompson libusb20_dev_close(struct libusb20_device *pdev) 580df4b8c2aSAndrew Thompson { 581df4b8c2aSAndrew Thompson struct libusb20_transfer *xfer; 582df4b8c2aSAndrew Thompson uint16_t x; 583df4b8c2aSAndrew Thompson int error = 0; 584df4b8c2aSAndrew Thompson 585df4b8c2aSAndrew Thompson if (!pdev->is_opened) { 586df4b8c2aSAndrew Thompson return (LIBUSB20_ERROR_OTHER); 587df4b8c2aSAndrew Thompson } 588df4b8c2aSAndrew Thompson for (x = 0; x != pdev->nTransfer; x++) { 589df4b8c2aSAndrew Thompson xfer = pdev->pTransfer + x; 590df4b8c2aSAndrew Thompson 5914594d907SAndrew Thompson if (!xfer->is_opened) { 5924594d907SAndrew Thompson /* transfer is not opened */ 5934594d907SAndrew Thompson continue; 5944594d907SAndrew Thompson } 5954594d907SAndrew Thompson 596df4b8c2aSAndrew Thompson libusb20_tr_drain(xfer); 5974594d907SAndrew Thompson 5984594d907SAndrew Thompson libusb20_tr_close(xfer); 599df4b8c2aSAndrew Thompson } 600df4b8c2aSAndrew Thompson 601df4b8c2aSAndrew Thompson if (pdev->pTransfer != NULL) { 602df4b8c2aSAndrew Thompson free(pdev->pTransfer); 603df4b8c2aSAndrew Thompson pdev->pTransfer = NULL; 604df4b8c2aSAndrew Thompson } 605df4b8c2aSAndrew Thompson error = pdev->beMethods->close_device(pdev); 606df4b8c2aSAndrew Thompson 607df4b8c2aSAndrew Thompson pdev->methods = &libusb20_dummy_methods; 608df4b8c2aSAndrew Thompson 609df4b8c2aSAndrew Thompson pdev->is_opened = 0; 610df4b8c2aSAndrew Thompson 6114315b3c9SAndrew Thompson /* 6124315b3c9SAndrew Thompson * The following variable is only used by the libusb v0.1 6134315b3c9SAndrew Thompson * compat layer: 6144315b3c9SAndrew Thompson */ 6154315b3c9SAndrew Thompson pdev->claimed_interface = 0; 616df4b8c2aSAndrew Thompson 6175b40d960SHans Petter Selasky /* 6185b40d960SHans Petter Selasky * The following variable is only used by the libusb v1.0 6195b40d960SHans Petter Selasky * compat layer: 6205b40d960SHans Petter Selasky */ 6215b40d960SHans Petter Selasky pdev->auto_detach = 0; 6225b40d960SHans Petter Selasky 623df4b8c2aSAndrew Thompson return (error); 624df4b8c2aSAndrew Thompson } 625df4b8c2aSAndrew Thompson 626df4b8c2aSAndrew Thompson int 627df4b8c2aSAndrew Thompson libusb20_dev_detach_kernel_driver(struct libusb20_device *pdev, uint8_t ifaceIndex) 628df4b8c2aSAndrew Thompson { 629df4b8c2aSAndrew Thompson int error; 630df4b8c2aSAndrew Thompson 631df4b8c2aSAndrew Thompson error = pdev->methods->detach_kernel_driver(pdev, ifaceIndex); 632df4b8c2aSAndrew Thompson return (error); 633df4b8c2aSAndrew Thompson } 634df4b8c2aSAndrew Thompson 635df4b8c2aSAndrew Thompson struct LIBUSB20_DEVICE_DESC_DECODED * 636df4b8c2aSAndrew Thompson libusb20_dev_get_device_desc(struct libusb20_device *pdev) 637df4b8c2aSAndrew Thompson { 638df4b8c2aSAndrew Thompson return (&(pdev->ddesc)); 639df4b8c2aSAndrew Thompson } 640df4b8c2aSAndrew Thompson 641df4b8c2aSAndrew Thompson int 642df4b8c2aSAndrew Thompson libusb20_dev_get_fd(struct libusb20_device *pdev) 643df4b8c2aSAndrew Thompson { 644df4b8c2aSAndrew Thompson return (pdev->file); 645df4b8c2aSAndrew Thompson } 646df4b8c2aSAndrew Thompson 647df4b8c2aSAndrew Thompson int 648df4b8c2aSAndrew Thompson libusb20_dev_kernel_driver_active(struct libusb20_device *pdev, uint8_t ifaceIndex) 649df4b8c2aSAndrew Thompson { 650df4b8c2aSAndrew Thompson int error; 651df4b8c2aSAndrew Thompson 652df4b8c2aSAndrew Thompson error = pdev->methods->kernel_driver_active(pdev, ifaceIndex); 653df4b8c2aSAndrew Thompson return (error); 654df4b8c2aSAndrew Thompson } 655df4b8c2aSAndrew Thompson 656df4b8c2aSAndrew Thompson int 657df4b8c2aSAndrew Thompson libusb20_dev_open(struct libusb20_device *pdev, uint16_t nTransferMax) 658df4b8c2aSAndrew Thompson { 659df4b8c2aSAndrew Thompson struct libusb20_transfer *xfer; 660df4b8c2aSAndrew Thompson uint32_t size; 661df4b8c2aSAndrew Thompson uint16_t x; 662df4b8c2aSAndrew Thompson int error; 663df4b8c2aSAndrew Thompson 664df4b8c2aSAndrew Thompson if (pdev->is_opened) { 665df4b8c2aSAndrew Thompson return (LIBUSB20_ERROR_BUSY); 666df4b8c2aSAndrew Thompson } 667df4b8c2aSAndrew Thompson if (nTransferMax >= 256) { 668df4b8c2aSAndrew Thompson return (LIBUSB20_ERROR_INVALID_PARAM); 669df4b8c2aSAndrew Thompson } else if (nTransferMax != 0) { 670df4b8c2aSAndrew Thompson size = sizeof(pdev->pTransfer[0]) * nTransferMax; 671df4b8c2aSAndrew Thompson pdev->pTransfer = malloc(size); 672df4b8c2aSAndrew Thompson if (pdev->pTransfer == NULL) { 673df4b8c2aSAndrew Thompson return (LIBUSB20_ERROR_NO_MEM); 674df4b8c2aSAndrew Thompson } 675df4b8c2aSAndrew Thompson memset(pdev->pTransfer, 0, size); 676df4b8c2aSAndrew Thompson } 677df4b8c2aSAndrew Thompson /* initialise all transfers */ 678df4b8c2aSAndrew Thompson for (x = 0; x != nTransferMax; x++) { 679df4b8c2aSAndrew Thompson 680df4b8c2aSAndrew Thompson xfer = pdev->pTransfer + x; 681df4b8c2aSAndrew Thompson 682df4b8c2aSAndrew Thompson xfer->pdev = pdev; 683df4b8c2aSAndrew Thompson xfer->trIndex = x; 684df4b8c2aSAndrew Thompson xfer->callback = &dummy_callback; 685df4b8c2aSAndrew Thompson } 686df4b8c2aSAndrew Thompson 687df4b8c2aSAndrew Thompson /* set "nTransfer" early */ 688df4b8c2aSAndrew Thompson pdev->nTransfer = nTransferMax; 689df4b8c2aSAndrew Thompson 690df4b8c2aSAndrew Thompson error = pdev->beMethods->open_device(pdev, nTransferMax); 691df4b8c2aSAndrew Thompson 692df4b8c2aSAndrew Thompson if (error) { 693df4b8c2aSAndrew Thompson if (pdev->pTransfer != NULL) { 694df4b8c2aSAndrew Thompson free(pdev->pTransfer); 695df4b8c2aSAndrew Thompson pdev->pTransfer = NULL; 696df4b8c2aSAndrew Thompson } 697df4b8c2aSAndrew Thompson pdev->file = -1; 698df4b8c2aSAndrew Thompson pdev->file_ctrl = -1; 699df4b8c2aSAndrew Thompson pdev->nTransfer = 0; 700df4b8c2aSAndrew Thompson } else { 701df4b8c2aSAndrew Thompson pdev->is_opened = 1; 702df4b8c2aSAndrew Thompson } 703df4b8c2aSAndrew Thompson return (error); 704df4b8c2aSAndrew Thompson } 705df4b8c2aSAndrew Thompson 706df4b8c2aSAndrew Thompson int 707df4b8c2aSAndrew Thompson libusb20_dev_reset(struct libusb20_device *pdev) 708df4b8c2aSAndrew Thompson { 709df4b8c2aSAndrew Thompson int error; 710df4b8c2aSAndrew Thompson 711df4b8c2aSAndrew Thompson error = pdev->methods->reset_device(pdev); 712df4b8c2aSAndrew Thompson return (error); 713df4b8c2aSAndrew Thompson } 714df4b8c2aSAndrew Thompson 715df4b8c2aSAndrew Thompson int 716e50ac68bSAndrew Thompson libusb20_dev_check_connected(struct libusb20_device *pdev) 717e50ac68bSAndrew Thompson { 718e50ac68bSAndrew Thompson int error; 719e50ac68bSAndrew Thompson 720e50ac68bSAndrew Thompson error = pdev->methods->check_connected(pdev); 721e50ac68bSAndrew Thompson return (error); 722e50ac68bSAndrew Thompson } 723e50ac68bSAndrew Thompson 724e50ac68bSAndrew Thompson int 725df4b8c2aSAndrew Thompson libusb20_dev_set_power_mode(struct libusb20_device *pdev, uint8_t power_mode) 726df4b8c2aSAndrew Thompson { 727df4b8c2aSAndrew Thompson int error; 728df4b8c2aSAndrew Thompson 729df4b8c2aSAndrew Thompson error = pdev->methods->set_power_mode(pdev, power_mode); 730df4b8c2aSAndrew Thompson return (error); 731df4b8c2aSAndrew Thompson } 732df4b8c2aSAndrew Thompson 733df4b8c2aSAndrew Thompson uint8_t 734df4b8c2aSAndrew Thompson libusb20_dev_get_power_mode(struct libusb20_device *pdev) 735df4b8c2aSAndrew Thompson { 736df4b8c2aSAndrew Thompson int error; 737df4b8c2aSAndrew Thompson uint8_t power_mode; 738df4b8c2aSAndrew Thompson 739df4b8c2aSAndrew Thompson error = pdev->methods->get_power_mode(pdev, &power_mode); 740df4b8c2aSAndrew Thompson if (error) 741df4b8c2aSAndrew Thompson power_mode = LIBUSB20_POWER_ON; /* fake power mode */ 742df4b8c2aSAndrew Thompson return (power_mode); 743df4b8c2aSAndrew Thompson } 744df4b8c2aSAndrew Thompson 745c77a24c2SHans Petter Selasky int 746c77a24c2SHans Petter Selasky libusb20_dev_get_port_path(struct libusb20_device *pdev, uint8_t *buf, uint8_t bufsize) 747c77a24c2SHans Petter Selasky { 748fd2ef04fSHans Petter Selasky 749fd2ef04fSHans Petter Selasky if (pdev->port_level == 0) { 750fd2ef04fSHans Petter Selasky /* 751fd2ef04fSHans Petter Selasky * Fallback for backends without port path: 752fd2ef04fSHans Petter Selasky */ 753fd2ef04fSHans Petter Selasky if (bufsize < 2) 754fd2ef04fSHans Petter Selasky return (LIBUSB20_ERROR_OVERFLOW); 755fd2ef04fSHans Petter Selasky buf[0] = pdev->parent_address; 756fd2ef04fSHans Petter Selasky buf[1] = pdev->parent_port; 757fd2ef04fSHans Petter Selasky return (2); 758fd2ef04fSHans Petter Selasky } 759fd2ef04fSHans Petter Selasky 760fd2ef04fSHans Petter Selasky /* check if client buffer is too small */ 761fd2ef04fSHans Petter Selasky if (pdev->port_level > bufsize) 762fd2ef04fSHans Petter Selasky return (LIBUSB20_ERROR_OVERFLOW); 763fd2ef04fSHans Petter Selasky 764fd2ef04fSHans Petter Selasky /* copy port number information */ 765fd2ef04fSHans Petter Selasky memcpy(buf, pdev->port_path, pdev->port_level); 766fd2ef04fSHans Petter Selasky 767fd2ef04fSHans Petter Selasky return (pdev->port_level); /* success */ 768c77a24c2SHans Petter Selasky } 769c77a24c2SHans Petter Selasky 770aafcb732SHans Petter Selasky uint16_t 771aafcb732SHans Petter Selasky libusb20_dev_get_power_usage(struct libusb20_device *pdev) 772aafcb732SHans Petter Selasky { 773aafcb732SHans Petter Selasky int error; 774aafcb732SHans Petter Selasky uint16_t power_usage; 775aafcb732SHans Petter Selasky 776aafcb732SHans Petter Selasky error = pdev->methods->get_power_usage(pdev, &power_usage); 777aafcb732SHans Petter Selasky if (error) 778aafcb732SHans Petter Selasky power_usage = 0; 779aafcb732SHans Petter Selasky return (power_usage); 780aafcb732SHans Petter Selasky } 781aafcb732SHans Petter Selasky 782df4b8c2aSAndrew Thompson int 783df4b8c2aSAndrew Thompson libusb20_dev_set_alt_index(struct libusb20_device *pdev, uint8_t ifaceIndex, uint8_t altIndex) 784df4b8c2aSAndrew Thompson { 785df4b8c2aSAndrew Thompson int error; 786df4b8c2aSAndrew Thompson 787df4b8c2aSAndrew Thompson error = pdev->methods->set_alt_index(pdev, ifaceIndex, altIndex); 788df4b8c2aSAndrew Thompson return (error); 789df4b8c2aSAndrew Thompson } 790df4b8c2aSAndrew Thompson 791df4b8c2aSAndrew Thompson int 792df4b8c2aSAndrew Thompson libusb20_dev_set_config_index(struct libusb20_device *pdev, uint8_t configIndex) 793df4b8c2aSAndrew Thompson { 794df4b8c2aSAndrew Thompson int error; 795df4b8c2aSAndrew Thompson 796df4b8c2aSAndrew Thompson error = pdev->methods->set_config_index(pdev, configIndex); 797df4b8c2aSAndrew Thompson return (error); 798df4b8c2aSAndrew Thompson } 799df4b8c2aSAndrew Thompson 800df4b8c2aSAndrew Thompson int 801df4b8c2aSAndrew Thompson libusb20_dev_request_sync(struct libusb20_device *pdev, 802df4b8c2aSAndrew Thompson struct LIBUSB20_CONTROL_SETUP_DECODED *setup, void *data, 803df4b8c2aSAndrew Thompson uint16_t *pactlen, uint32_t timeout, uint8_t flags) 804df4b8c2aSAndrew Thompson { 805df4b8c2aSAndrew Thompson int error; 806df4b8c2aSAndrew Thompson 807df4b8c2aSAndrew Thompson error = pdev->methods->do_request_sync(pdev, 808df4b8c2aSAndrew Thompson setup, data, pactlen, timeout, flags); 809df4b8c2aSAndrew Thompson return (error); 810df4b8c2aSAndrew Thompson } 811df4b8c2aSAndrew Thompson 812df4b8c2aSAndrew Thompson int 813df4b8c2aSAndrew Thompson libusb20_dev_req_string_sync(struct libusb20_device *pdev, 814df4b8c2aSAndrew Thompson uint8_t str_index, uint16_t langid, void *ptr, uint16_t len) 815df4b8c2aSAndrew Thompson { 816df4b8c2aSAndrew Thompson struct LIBUSB20_CONTROL_SETUP_DECODED req; 817df4b8c2aSAndrew Thompson int error; 818dfd30b26SHans Petter Selasky int flags; 819df4b8c2aSAndrew Thompson 820ccef4ddfSAndrew Thompson /* make sure memory is initialised */ 821ccef4ddfSAndrew Thompson memset(ptr, 0, len); 822ccef4ddfSAndrew Thompson 823df4b8c2aSAndrew Thompson if (len < 4) { 824df4b8c2aSAndrew Thompson /* invalid length */ 825df4b8c2aSAndrew Thompson return (LIBUSB20_ERROR_INVALID_PARAM); 826df4b8c2aSAndrew Thompson } 827df4b8c2aSAndrew Thompson LIBUSB20_INIT(LIBUSB20_CONTROL_SETUP, &req); 828df4b8c2aSAndrew Thompson 829df4b8c2aSAndrew Thompson /* 830df4b8c2aSAndrew Thompson * We need to read the USB string in two steps else some USB 831df4b8c2aSAndrew Thompson * devices will complain. 832df4b8c2aSAndrew Thompson */ 833df4b8c2aSAndrew Thompson req.bmRequestType = 834df4b8c2aSAndrew Thompson LIBUSB20_REQUEST_TYPE_STANDARD | 835df4b8c2aSAndrew Thompson LIBUSB20_RECIPIENT_DEVICE | 836df4b8c2aSAndrew Thompson LIBUSB20_ENDPOINT_IN; 837df4b8c2aSAndrew Thompson req.bRequest = LIBUSB20_REQUEST_GET_DESCRIPTOR; 838df4b8c2aSAndrew Thompson req.wValue = (LIBUSB20_DT_STRING << 8) | str_index; 839df4b8c2aSAndrew Thompson req.wIndex = langid; 840df4b8c2aSAndrew Thompson req.wLength = 4; /* bytes */ 841df4b8c2aSAndrew Thompson 842df4b8c2aSAndrew Thompson error = libusb20_dev_request_sync(pdev, &req, 843df4b8c2aSAndrew Thompson ptr, NULL, 1000, LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK); 844df4b8c2aSAndrew Thompson if (error) { 845dfd30b26SHans Petter Selasky /* try to request full string */ 846dfd30b26SHans Petter Selasky req.wLength = 255; 847dfd30b26SHans Petter Selasky flags = 0; 848dfd30b26SHans Petter Selasky } else { 849dfd30b26SHans Petter Selasky /* extract length and request full string */ 850dfd30b26SHans Petter Selasky req.wLength = *(uint8_t *)ptr; 851dfd30b26SHans Petter Selasky flags = LIBUSB20_TRANSFER_SINGLE_SHORT_NOT_OK; 852df4b8c2aSAndrew Thompson } 853df4b8c2aSAndrew Thompson if (req.wLength > len) { 854df4b8c2aSAndrew Thompson /* partial string read */ 855df4b8c2aSAndrew Thompson req.wLength = len; 856df4b8c2aSAndrew Thompson } 857dfd30b26SHans Petter Selasky error = libusb20_dev_request_sync(pdev, &req, ptr, NULL, 1000, flags); 858dfd30b26SHans Petter Selasky if (error) 859df4b8c2aSAndrew Thompson return (error); 860dfd30b26SHans Petter Selasky 861dfd30b26SHans Petter Selasky if (((uint8_t *)ptr)[1] != LIBUSB20_DT_STRING) 862df4b8c2aSAndrew Thompson return (LIBUSB20_ERROR_OTHER); 863df4b8c2aSAndrew Thompson return (0); /* success */ 864df4b8c2aSAndrew Thompson } 865df4b8c2aSAndrew Thompson 866df4b8c2aSAndrew Thompson int 867df4b8c2aSAndrew Thompson libusb20_dev_req_string_simple_sync(struct libusb20_device *pdev, 868df4b8c2aSAndrew Thompson uint8_t str_index, void *ptr, uint16_t len) 869df4b8c2aSAndrew Thompson { 870df4b8c2aSAndrew Thompson char *buf; 871df4b8c2aSAndrew Thompson int error; 872df4b8c2aSAndrew Thompson uint16_t langid; 873df4b8c2aSAndrew Thompson uint16_t n; 874df4b8c2aSAndrew Thompson uint16_t i; 875df4b8c2aSAndrew Thompson uint16_t c; 876df4b8c2aSAndrew Thompson uint8_t temp[255]; 877df4b8c2aSAndrew Thompson uint8_t swap; 878df4b8c2aSAndrew Thompson 879df4b8c2aSAndrew Thompson /* the following code derives from the FreeBSD USB kernel */ 880df4b8c2aSAndrew Thompson 881df4b8c2aSAndrew Thompson if ((len < 1) || (ptr == NULL)) { 882df4b8c2aSAndrew Thompson /* too short buffer */ 883df4b8c2aSAndrew Thompson return (LIBUSB20_ERROR_INVALID_PARAM); 884df4b8c2aSAndrew Thompson } 885df4b8c2aSAndrew Thompson error = libusb20_dev_req_string_sync(pdev, 886df4b8c2aSAndrew Thompson 0, 0, temp, sizeof(temp)); 887df4b8c2aSAndrew Thompson if (error < 0) { 888df4b8c2aSAndrew Thompson *(uint8_t *)ptr = 0; /* zero terminate */ 889df4b8c2aSAndrew Thompson return (error); 890df4b8c2aSAndrew Thompson } 891df4b8c2aSAndrew Thompson langid = temp[2] | (temp[3] << 8); 892df4b8c2aSAndrew Thompson 893df4b8c2aSAndrew Thompson error = libusb20_dev_req_string_sync(pdev, str_index, 894df4b8c2aSAndrew Thompson langid, temp, sizeof(temp)); 895df4b8c2aSAndrew Thompson if (error < 0) { 896df4b8c2aSAndrew Thompson *(uint8_t *)ptr = 0; /* zero terminate */ 897df4b8c2aSAndrew Thompson return (error); 898df4b8c2aSAndrew Thompson } 899df4b8c2aSAndrew Thompson if (temp[0] < 2) { 900df4b8c2aSAndrew Thompson /* string length is too short */ 901df4b8c2aSAndrew Thompson *(uint8_t *)ptr = 0; /* zero terminate */ 902df4b8c2aSAndrew Thompson return (LIBUSB20_ERROR_OTHER); 903df4b8c2aSAndrew Thompson } 904df4b8c2aSAndrew Thompson /* reserve one byte for terminating zero */ 905df4b8c2aSAndrew Thompson len--; 906df4b8c2aSAndrew Thompson 907df4b8c2aSAndrew Thompson /* find maximum length */ 908df4b8c2aSAndrew Thompson n = (temp[0] / 2) - 1; 909df4b8c2aSAndrew Thompson if (n > len) { 910df4b8c2aSAndrew Thompson n = len; 911df4b8c2aSAndrew Thompson } 912df4b8c2aSAndrew Thompson /* reset swap state */ 913df4b8c2aSAndrew Thompson swap = 3; 914df4b8c2aSAndrew Thompson 915df4b8c2aSAndrew Thompson /* setup output buffer pointer */ 916df4b8c2aSAndrew Thompson buf = ptr; 917df4b8c2aSAndrew Thompson 918df4b8c2aSAndrew Thompson /* convert and filter */ 919df4b8c2aSAndrew Thompson for (i = 0; (i != n); i++) { 920df4b8c2aSAndrew Thompson c = temp[(2 * i) + 2] | (temp[(2 * i) + 3] << 8); 921df4b8c2aSAndrew Thompson 922df4b8c2aSAndrew Thompson /* convert from Unicode, handle buggy strings */ 923df4b8c2aSAndrew Thompson if (((c & 0xff00) == 0) && (swap & 1)) { 924df4b8c2aSAndrew Thompson /* Little Endian, default */ 925df4b8c2aSAndrew Thompson *buf = c; 926df4b8c2aSAndrew Thompson swap = 1; 927df4b8c2aSAndrew Thompson } else if (((c & 0x00ff) == 0) && (swap & 2)) { 928df4b8c2aSAndrew Thompson /* Big Endian */ 929df4b8c2aSAndrew Thompson *buf = c >> 8; 930df4b8c2aSAndrew Thompson swap = 2; 931df4b8c2aSAndrew Thompson } else { 932df4b8c2aSAndrew Thompson /* skip invalid character */ 933df4b8c2aSAndrew Thompson continue; 934df4b8c2aSAndrew Thompson } 935df4b8c2aSAndrew Thompson /* 936df4b8c2aSAndrew Thompson * Filter by default - we don't allow greater and less than 937df4b8c2aSAndrew Thompson * signs because they might confuse the dmesg printouts! 938df4b8c2aSAndrew Thompson */ 939df4b8c2aSAndrew Thompson if ((*buf == '<') || (*buf == '>') || (!isprint(*buf))) { 940df4b8c2aSAndrew Thompson /* skip invalid character */ 941df4b8c2aSAndrew Thompson continue; 942df4b8c2aSAndrew Thompson } 943df4b8c2aSAndrew Thompson buf++; 944df4b8c2aSAndrew Thompson } 945df4b8c2aSAndrew Thompson *buf = 0; /* zero terminate string */ 946df4b8c2aSAndrew Thompson 947df4b8c2aSAndrew Thompson return (0); 948df4b8c2aSAndrew Thompson } 949df4b8c2aSAndrew Thompson 950df4b8c2aSAndrew Thompson struct libusb20_config * 951df4b8c2aSAndrew Thompson libusb20_dev_alloc_config(struct libusb20_device *pdev, uint8_t configIndex) 952df4b8c2aSAndrew Thompson { 953df4b8c2aSAndrew Thompson struct libusb20_config *retval = NULL; 954df4b8c2aSAndrew Thompson uint8_t *ptr; 955df4b8c2aSAndrew Thompson uint16_t len; 956df4b8c2aSAndrew Thompson uint8_t do_close; 957df4b8c2aSAndrew Thompson int error; 958df4b8c2aSAndrew Thompson 95908216d18SHans Petter Selasky /* 96008216d18SHans Petter Selasky * Catch invalid configuration descriptor reads early on to 96108216d18SHans Petter Selasky * avoid issues with devices that don't check for a valid USB 96208216d18SHans Petter Selasky * configuration read request. 96308216d18SHans Petter Selasky */ 96408216d18SHans Petter Selasky if (configIndex >= pdev->ddesc.bNumConfigurations) 96508216d18SHans Petter Selasky return (NULL); 96608216d18SHans Petter Selasky 967df4b8c2aSAndrew Thompson if (!pdev->is_opened) { 968df4b8c2aSAndrew Thompson error = libusb20_dev_open(pdev, 0); 969df4b8c2aSAndrew Thompson if (error) { 970df4b8c2aSAndrew Thompson return (NULL); 971df4b8c2aSAndrew Thompson } 972df4b8c2aSAndrew Thompson do_close = 1; 973df4b8c2aSAndrew Thompson } else { 974df4b8c2aSAndrew Thompson do_close = 0; 975df4b8c2aSAndrew Thompson } 976df4b8c2aSAndrew Thompson error = pdev->methods->get_config_desc_full(pdev, 977df4b8c2aSAndrew Thompson &ptr, &len, configIndex); 978df4b8c2aSAndrew Thompson 979df4b8c2aSAndrew Thompson if (error) { 980df4b8c2aSAndrew Thompson goto done; 981df4b8c2aSAndrew Thompson } 982df4b8c2aSAndrew Thompson /* parse new config descriptor */ 983df4b8c2aSAndrew Thompson retval = libusb20_parse_config_desc(ptr); 984df4b8c2aSAndrew Thompson 985df4b8c2aSAndrew Thompson /* free config descriptor */ 986df4b8c2aSAndrew Thompson free(ptr); 987df4b8c2aSAndrew Thompson 988df4b8c2aSAndrew Thompson done: 989df4b8c2aSAndrew Thompson if (do_close) { 990df4b8c2aSAndrew Thompson error = libusb20_dev_close(pdev); 991df4b8c2aSAndrew Thompson } 992df4b8c2aSAndrew Thompson return (retval); 993df4b8c2aSAndrew Thompson } 994df4b8c2aSAndrew Thompson 995df4b8c2aSAndrew Thompson struct libusb20_device * 996df4b8c2aSAndrew Thompson libusb20_dev_alloc(void) 997df4b8c2aSAndrew Thompson { 998df4b8c2aSAndrew Thompson struct libusb20_device *pdev; 999df4b8c2aSAndrew Thompson 1000df4b8c2aSAndrew Thompson pdev = malloc(sizeof(*pdev)); 1001df4b8c2aSAndrew Thompson if (pdev == NULL) { 1002df4b8c2aSAndrew Thompson return (NULL); 1003df4b8c2aSAndrew Thompson } 1004df4b8c2aSAndrew Thompson memset(pdev, 0, sizeof(*pdev)); 1005df4b8c2aSAndrew Thompson 1006df4b8c2aSAndrew Thompson pdev->file = -1; 1007df4b8c2aSAndrew Thompson pdev->file_ctrl = -1; 1008df4b8c2aSAndrew Thompson pdev->methods = &libusb20_dummy_methods; 1009df4b8c2aSAndrew Thompson return (pdev); 1010df4b8c2aSAndrew Thompson } 1011df4b8c2aSAndrew Thompson 1012df4b8c2aSAndrew Thompson uint8_t 1013df4b8c2aSAndrew Thompson libusb20_dev_get_config_index(struct libusb20_device *pdev) 1014df4b8c2aSAndrew Thompson { 1015df4b8c2aSAndrew Thompson int error; 1016df4b8c2aSAndrew Thompson uint8_t cfg_index; 1017df4b8c2aSAndrew Thompson uint8_t do_close; 1018df4b8c2aSAndrew Thompson 1019df4b8c2aSAndrew Thompson if (!pdev->is_opened) { 1020df4b8c2aSAndrew Thompson error = libusb20_dev_open(pdev, 0); 1021df4b8c2aSAndrew Thompson if (error == 0) { 1022df4b8c2aSAndrew Thompson do_close = 1; 1023df4b8c2aSAndrew Thompson } else { 1024df4b8c2aSAndrew Thompson do_close = 0; 1025df4b8c2aSAndrew Thompson } 1026df4b8c2aSAndrew Thompson } else { 1027df4b8c2aSAndrew Thompson do_close = 0; 1028df4b8c2aSAndrew Thompson } 1029df4b8c2aSAndrew Thompson 1030df4b8c2aSAndrew Thompson error = pdev->methods->get_config_index(pdev, &cfg_index); 1031d81535d1SHans Petter Selasky if (error) 1032d81535d1SHans Petter Selasky cfg_index = 0xFF; /* current config index */ 1033df4b8c2aSAndrew Thompson if (do_close) { 1034df4b8c2aSAndrew Thompson if (libusb20_dev_close(pdev)) { 1035df4b8c2aSAndrew Thompson /* ignore */ 1036df4b8c2aSAndrew Thompson } 1037df4b8c2aSAndrew Thompson } 1038df4b8c2aSAndrew Thompson return (cfg_index); 1039df4b8c2aSAndrew Thompson } 1040df4b8c2aSAndrew Thompson 1041df4b8c2aSAndrew Thompson uint8_t 1042df4b8c2aSAndrew Thompson libusb20_dev_get_mode(struct libusb20_device *pdev) 1043df4b8c2aSAndrew Thompson { 1044df4b8c2aSAndrew Thompson return (pdev->usb_mode); 1045df4b8c2aSAndrew Thompson } 1046df4b8c2aSAndrew Thompson 1047df4b8c2aSAndrew Thompson uint8_t 1048df4b8c2aSAndrew Thompson libusb20_dev_get_speed(struct libusb20_device *pdev) 1049df4b8c2aSAndrew Thompson { 1050df4b8c2aSAndrew Thompson return (pdev->usb_speed); 1051df4b8c2aSAndrew Thompson } 1052df4b8c2aSAndrew Thompson 1053*34b0ca24SHans Petter Selasky int 1054*34b0ca24SHans Petter Selasky libusb20_dev_get_stats(struct libusb20_device *pdev, struct libusb20_device_stats *pstats) 1055*34b0ca24SHans Petter Selasky { 1056*34b0ca24SHans Petter Selasky uint8_t do_close; 1057*34b0ca24SHans Petter Selasky int error; 1058*34b0ca24SHans Petter Selasky 1059*34b0ca24SHans Petter Selasky if (!pdev->is_opened) { 1060*34b0ca24SHans Petter Selasky error = libusb20_dev_open(pdev, 0); 1061*34b0ca24SHans Petter Selasky if (error == 0) { 1062*34b0ca24SHans Petter Selasky do_close = 1; 1063*34b0ca24SHans Petter Selasky } else { 1064*34b0ca24SHans Petter Selasky do_close = 0; 1065*34b0ca24SHans Petter Selasky } 1066*34b0ca24SHans Petter Selasky } else { 1067*34b0ca24SHans Petter Selasky do_close = 0; 1068*34b0ca24SHans Petter Selasky } 1069*34b0ca24SHans Petter Selasky 1070*34b0ca24SHans Petter Selasky error = pdev->methods->get_stats(pdev, pstats); 1071*34b0ca24SHans Petter Selasky 1072*34b0ca24SHans Petter Selasky if (do_close) 1073*34b0ca24SHans Petter Selasky (void) libusb20_dev_close(pdev); 1074*34b0ca24SHans Petter Selasky 1075*34b0ca24SHans Petter Selasky return (error); 1076*34b0ca24SHans Petter Selasky } 1077*34b0ca24SHans Petter Selasky 1078df4b8c2aSAndrew Thompson /* if this function returns an error, the device is gone */ 1079df4b8c2aSAndrew Thompson int 1080df4b8c2aSAndrew Thompson libusb20_dev_process(struct libusb20_device *pdev) 1081df4b8c2aSAndrew Thompson { 1082df4b8c2aSAndrew Thompson int error; 1083df4b8c2aSAndrew Thompson 1084df4b8c2aSAndrew Thompson error = pdev->methods->process(pdev); 1085df4b8c2aSAndrew Thompson return (error); 1086df4b8c2aSAndrew Thompson } 1087df4b8c2aSAndrew Thompson 1088df4b8c2aSAndrew Thompson void 1089df4b8c2aSAndrew Thompson libusb20_dev_wait_process(struct libusb20_device *pdev, int timeout) 1090df4b8c2aSAndrew Thompson { 1091df4b8c2aSAndrew Thompson struct pollfd pfd[1]; 1092df4b8c2aSAndrew Thompson 1093df4b8c2aSAndrew Thompson if (!pdev->is_opened) { 1094df4b8c2aSAndrew Thompson return; 1095df4b8c2aSAndrew Thompson } 1096df4b8c2aSAndrew Thompson pfd[0].fd = pdev->file; 1097df4b8c2aSAndrew Thompson pfd[0].events = (POLLIN | POLLOUT | POLLRDNORM | POLLWRNORM); 1098df4b8c2aSAndrew Thompson pfd[0].revents = 0; 1099df4b8c2aSAndrew Thompson 1100df4b8c2aSAndrew Thompson if (poll(pfd, 1, timeout)) { 1101df4b8c2aSAndrew Thompson /* ignore any error */ 1102df4b8c2aSAndrew Thompson } 1103df4b8c2aSAndrew Thompson return; 1104df4b8c2aSAndrew Thompson } 1105df4b8c2aSAndrew Thompson 1106df4b8c2aSAndrew Thompson void 1107df4b8c2aSAndrew Thompson libusb20_dev_free(struct libusb20_device *pdev) 1108df4b8c2aSAndrew Thompson { 1109df4b8c2aSAndrew Thompson if (pdev == NULL) { 1110df4b8c2aSAndrew Thompson /* be NULL safe */ 1111df4b8c2aSAndrew Thompson return; 1112df4b8c2aSAndrew Thompson } 1113df4b8c2aSAndrew Thompson if (pdev->is_opened) { 1114df4b8c2aSAndrew Thompson if (libusb20_dev_close(pdev)) { 1115df4b8c2aSAndrew Thompson /* ignore any errors */ 1116df4b8c2aSAndrew Thompson } 1117df4b8c2aSAndrew Thompson } 1118df4b8c2aSAndrew Thompson free(pdev); 1119df4b8c2aSAndrew Thompson return; 1120df4b8c2aSAndrew Thompson } 1121df4b8c2aSAndrew Thompson 1122df4b8c2aSAndrew Thompson int 1123df4b8c2aSAndrew Thompson libusb20_dev_get_info(struct libusb20_device *pdev, 1124760bc48eSAndrew Thompson struct usb_device_info *pinfo) 1125df4b8c2aSAndrew Thompson { 1126df4b8c2aSAndrew Thompson if (pinfo == NULL) 1127df4b8c2aSAndrew Thompson return (LIBUSB20_ERROR_INVALID_PARAM); 1128df4b8c2aSAndrew Thompson 1129df4b8c2aSAndrew Thompson return (pdev->beMethods->dev_get_info(pdev, pinfo)); 1130df4b8c2aSAndrew Thompson } 1131df4b8c2aSAndrew Thompson 1132df4b8c2aSAndrew Thompson const char * 1133df4b8c2aSAndrew Thompson libusb20_dev_get_backend_name(struct libusb20_device *pdev) 1134df4b8c2aSAndrew Thompson { 1135df4b8c2aSAndrew Thompson return (pdev->beMethods->get_backend_name()); 1136df4b8c2aSAndrew Thompson } 1137df4b8c2aSAndrew Thompson 1138df4b8c2aSAndrew Thompson const char * 1139df4b8c2aSAndrew Thompson libusb20_dev_get_desc(struct libusb20_device *pdev) 1140df4b8c2aSAndrew Thompson { 1141df4b8c2aSAndrew Thompson return (pdev->usb_desc); 1142df4b8c2aSAndrew Thompson } 1143df4b8c2aSAndrew Thompson 1144df4b8c2aSAndrew Thompson void 1145df4b8c2aSAndrew Thompson libusb20_dev_set_debug(struct libusb20_device *pdev, int debug) 1146df4b8c2aSAndrew Thompson { 1147df4b8c2aSAndrew Thompson pdev->debug = debug; 1148df4b8c2aSAndrew Thompson return; 1149df4b8c2aSAndrew Thompson } 1150df4b8c2aSAndrew Thompson 1151df4b8c2aSAndrew Thompson int 1152df4b8c2aSAndrew Thompson libusb20_dev_get_debug(struct libusb20_device *pdev) 1153df4b8c2aSAndrew Thompson { 1154df4b8c2aSAndrew Thompson return (pdev->debug); 1155df4b8c2aSAndrew Thompson } 1156df4b8c2aSAndrew Thompson 1157df4b8c2aSAndrew Thompson uint8_t 1158df4b8c2aSAndrew Thompson libusb20_dev_get_address(struct libusb20_device *pdev) 1159df4b8c2aSAndrew Thompson { 1160df4b8c2aSAndrew Thompson return (pdev->device_address); 1161df4b8c2aSAndrew Thompson } 1162df4b8c2aSAndrew Thompson 1163df4b8c2aSAndrew Thompson uint8_t 11642485d8a7SHans Petter Selasky libusb20_dev_get_parent_address(struct libusb20_device *pdev) 11652485d8a7SHans Petter Selasky { 11662485d8a7SHans Petter Selasky return (pdev->parent_address); 11672485d8a7SHans Petter Selasky } 11682485d8a7SHans Petter Selasky 11692485d8a7SHans Petter Selasky uint8_t 11702485d8a7SHans Petter Selasky libusb20_dev_get_parent_port(struct libusb20_device *pdev) 11712485d8a7SHans Petter Selasky { 11722485d8a7SHans Petter Selasky return (pdev->parent_port); 11732485d8a7SHans Petter Selasky } 11742485d8a7SHans Petter Selasky 11752485d8a7SHans Petter Selasky uint8_t 1176df4b8c2aSAndrew Thompson libusb20_dev_get_bus_number(struct libusb20_device *pdev) 1177df4b8c2aSAndrew Thompson { 1178df4b8c2aSAndrew Thompson return (pdev->bus_number); 1179df4b8c2aSAndrew Thompson } 1180df4b8c2aSAndrew Thompson 1181df4b8c2aSAndrew Thompson int 1182df4b8c2aSAndrew Thompson libusb20_dev_get_iface_desc(struct libusb20_device *pdev, 1183df4b8c2aSAndrew Thompson uint8_t iface_index, char *buf, uint8_t len) 1184df4b8c2aSAndrew Thompson { 1185df4b8c2aSAndrew Thompson if ((buf == NULL) || (len == 0)) 1186df4b8c2aSAndrew Thompson return (LIBUSB20_ERROR_INVALID_PARAM); 1187df4b8c2aSAndrew Thompson 11884eb5923dSHans Petter Selasky buf[0] = 0; /* set default string value */ 11894eb5923dSHans Petter Selasky 1190df4b8c2aSAndrew Thompson return (pdev->beMethods->dev_get_iface_desc( 1191df4b8c2aSAndrew Thompson pdev, iface_index, buf, len)); 1192df4b8c2aSAndrew Thompson } 1193df4b8c2aSAndrew Thompson 1194df4b8c2aSAndrew Thompson /* USB backend operations */ 1195df4b8c2aSAndrew Thompson 1196df4b8c2aSAndrew Thompson int 1197df4b8c2aSAndrew Thompson libusb20_be_get_dev_quirk(struct libusb20_backend *pbe, 1198df4b8c2aSAndrew Thompson uint16_t quirk_index, struct libusb20_quirk *pq) 1199df4b8c2aSAndrew Thompson { 1200df4b8c2aSAndrew Thompson return (pbe->methods->root_get_dev_quirk(pbe, quirk_index, pq)); 1201df4b8c2aSAndrew Thompson } 1202df4b8c2aSAndrew Thompson 1203df4b8c2aSAndrew Thompson int 1204df4b8c2aSAndrew Thompson libusb20_be_get_quirk_name(struct libusb20_backend *pbe, 1205df4b8c2aSAndrew Thompson uint16_t quirk_index, struct libusb20_quirk *pq) 1206df4b8c2aSAndrew Thompson { 1207df4b8c2aSAndrew Thompson return (pbe->methods->root_get_quirk_name(pbe, quirk_index, pq)); 1208df4b8c2aSAndrew Thompson } 1209df4b8c2aSAndrew Thompson 1210df4b8c2aSAndrew Thompson int 1211df4b8c2aSAndrew Thompson libusb20_be_add_dev_quirk(struct libusb20_backend *pbe, 1212df4b8c2aSAndrew Thompson struct libusb20_quirk *pq) 1213df4b8c2aSAndrew Thompson { 1214df4b8c2aSAndrew Thompson return (pbe->methods->root_add_dev_quirk(pbe, pq)); 1215df4b8c2aSAndrew Thompson } 1216df4b8c2aSAndrew Thompson 1217df4b8c2aSAndrew Thompson int 1218df4b8c2aSAndrew Thompson libusb20_be_remove_dev_quirk(struct libusb20_backend *pbe, 1219df4b8c2aSAndrew Thompson struct libusb20_quirk *pq) 1220df4b8c2aSAndrew Thompson { 1221df4b8c2aSAndrew Thompson return (pbe->methods->root_remove_dev_quirk(pbe, pq)); 1222df4b8c2aSAndrew Thompson } 1223df4b8c2aSAndrew Thompson 1224df4b8c2aSAndrew Thompson int 1225df4b8c2aSAndrew Thompson libusb20_be_set_template(struct libusb20_backend *pbe, int temp) 1226df4b8c2aSAndrew Thompson { 1227df4b8c2aSAndrew Thompson return (pbe->methods->root_set_template(pbe, temp)); 1228df4b8c2aSAndrew Thompson } 1229df4b8c2aSAndrew Thompson 1230df4b8c2aSAndrew Thompson int 1231df4b8c2aSAndrew Thompson libusb20_be_get_template(struct libusb20_backend *pbe, int *ptemp) 1232df4b8c2aSAndrew Thompson { 1233df4b8c2aSAndrew Thompson int temp; 1234df4b8c2aSAndrew Thompson 1235df4b8c2aSAndrew Thompson if (ptemp == NULL) 1236df4b8c2aSAndrew Thompson ptemp = &temp; 1237df4b8c2aSAndrew Thompson 1238df4b8c2aSAndrew Thompson return (pbe->methods->root_get_template(pbe, ptemp)); 1239df4b8c2aSAndrew Thompson } 1240df4b8c2aSAndrew Thompson 1241df4b8c2aSAndrew Thompson struct libusb20_device * 1242df4b8c2aSAndrew Thompson libusb20_be_device_foreach(struct libusb20_backend *pbe, struct libusb20_device *pdev) 1243df4b8c2aSAndrew Thompson { 1244df4b8c2aSAndrew Thompson if (pbe == NULL) { 1245df4b8c2aSAndrew Thompson pdev = NULL; 1246df4b8c2aSAndrew Thompson } else if (pdev == NULL) { 1247df4b8c2aSAndrew Thompson pdev = TAILQ_FIRST(&(pbe->usb_devs)); 1248df4b8c2aSAndrew Thompson } else { 1249df4b8c2aSAndrew Thompson pdev = TAILQ_NEXT(pdev, dev_entry); 1250df4b8c2aSAndrew Thompson } 1251df4b8c2aSAndrew Thompson return (pdev); 1252df4b8c2aSAndrew Thompson } 1253df4b8c2aSAndrew Thompson 1254df4b8c2aSAndrew Thompson struct libusb20_backend * 1255df4b8c2aSAndrew Thompson libusb20_be_alloc(const struct libusb20_backend_methods *methods) 1256df4b8c2aSAndrew Thompson { 1257df4b8c2aSAndrew Thompson struct libusb20_backend *pbe; 1258df4b8c2aSAndrew Thompson 1259df4b8c2aSAndrew Thompson pbe = malloc(sizeof(*pbe)); 1260df4b8c2aSAndrew Thompson if (pbe == NULL) { 1261df4b8c2aSAndrew Thompson return (NULL); 1262df4b8c2aSAndrew Thompson } 1263df4b8c2aSAndrew Thompson memset(pbe, 0, sizeof(*pbe)); 1264df4b8c2aSAndrew Thompson 1265df4b8c2aSAndrew Thompson TAILQ_INIT(&(pbe->usb_devs)); 1266df4b8c2aSAndrew Thompson 1267df4b8c2aSAndrew Thompson pbe->methods = methods; /* set backend methods */ 1268df4b8c2aSAndrew Thompson 1269df4b8c2aSAndrew Thompson /* do the initial device scan */ 1270df4b8c2aSAndrew Thompson if (pbe->methods->init_backend) { 1271df4b8c2aSAndrew Thompson pbe->methods->init_backend(pbe); 1272df4b8c2aSAndrew Thompson } 1273df4b8c2aSAndrew Thompson return (pbe); 1274df4b8c2aSAndrew Thompson } 1275df4b8c2aSAndrew Thompson 1276df4b8c2aSAndrew Thompson struct libusb20_backend * 1277df4b8c2aSAndrew Thompson libusb20_be_alloc_linux(void) 1278df4b8c2aSAndrew Thompson { 127999cd1f32SHans Petter Selasky return (NULL); 1280df4b8c2aSAndrew Thompson } 1281df4b8c2aSAndrew Thompson 1282df4b8c2aSAndrew Thompson struct libusb20_backend * 1283df4b8c2aSAndrew Thompson libusb20_be_alloc_ugen20(void) 1284df4b8c2aSAndrew Thompson { 128599cd1f32SHans Petter Selasky return (libusb20_be_alloc(&libusb20_ugen20_backend)); 1286df4b8c2aSAndrew Thompson } 1287df4b8c2aSAndrew Thompson 1288df4b8c2aSAndrew Thompson struct libusb20_backend * 1289df4b8c2aSAndrew Thompson libusb20_be_alloc_default(void) 1290df4b8c2aSAndrew Thompson { 1291df4b8c2aSAndrew Thompson struct libusb20_backend *pbe; 1292df4b8c2aSAndrew Thompson 129399cd1f32SHans Petter Selasky #ifdef __linux__ 1294df4b8c2aSAndrew Thompson pbe = libusb20_be_alloc_linux(); 1295df4b8c2aSAndrew Thompson if (pbe) { 1296df4b8c2aSAndrew Thompson return (pbe); 1297df4b8c2aSAndrew Thompson } 129899cd1f32SHans Petter Selasky #endif 1299df4b8c2aSAndrew Thompson pbe = libusb20_be_alloc_ugen20(); 1300df4b8c2aSAndrew Thompson if (pbe) { 1301df4b8c2aSAndrew Thompson return (pbe); 1302df4b8c2aSAndrew Thompson } 1303df4b8c2aSAndrew Thompson return (NULL); /* no backend found */ 1304df4b8c2aSAndrew Thompson } 1305df4b8c2aSAndrew Thompson 1306df4b8c2aSAndrew Thompson void 1307df4b8c2aSAndrew Thompson libusb20_be_free(struct libusb20_backend *pbe) 1308df4b8c2aSAndrew Thompson { 1309df4b8c2aSAndrew Thompson struct libusb20_device *pdev; 1310df4b8c2aSAndrew Thompson 1311df4b8c2aSAndrew Thompson if (pbe == NULL) { 1312df4b8c2aSAndrew Thompson /* be NULL safe */ 1313df4b8c2aSAndrew Thompson return; 1314df4b8c2aSAndrew Thompson } 1315df4b8c2aSAndrew Thompson while ((pdev = libusb20_be_device_foreach(pbe, NULL))) { 1316df4b8c2aSAndrew Thompson libusb20_be_dequeue_device(pbe, pdev); 1317df4b8c2aSAndrew Thompson libusb20_dev_free(pdev); 1318df4b8c2aSAndrew Thompson } 1319df4b8c2aSAndrew Thompson if (pbe->methods->exit_backend) { 1320df4b8c2aSAndrew Thompson pbe->methods->exit_backend(pbe); 1321df4b8c2aSAndrew Thompson } 1322ccef4ddfSAndrew Thompson /* free backend */ 1323ccef4ddfSAndrew Thompson free(pbe); 1324df4b8c2aSAndrew Thompson } 1325df4b8c2aSAndrew Thompson 1326df4b8c2aSAndrew Thompson void 1327df4b8c2aSAndrew Thompson libusb20_be_enqueue_device(struct libusb20_backend *pbe, struct libusb20_device *pdev) 1328df4b8c2aSAndrew Thompson { 1329df4b8c2aSAndrew Thompson pdev->beMethods = pbe->methods; /* copy backend methods */ 1330df4b8c2aSAndrew Thompson TAILQ_INSERT_TAIL(&(pbe->usb_devs), pdev, dev_entry); 1331df4b8c2aSAndrew Thompson } 1332df4b8c2aSAndrew Thompson 1333df4b8c2aSAndrew Thompson void 1334df4b8c2aSAndrew Thompson libusb20_be_dequeue_device(struct libusb20_backend *pbe, 1335df4b8c2aSAndrew Thompson struct libusb20_device *pdev) 1336df4b8c2aSAndrew Thompson { 1337df4b8c2aSAndrew Thompson TAILQ_REMOVE(&(pbe->usb_devs), pdev, dev_entry); 1338df4b8c2aSAndrew Thompson } 1339c61f2561SHans Petter Selasky 1340c61f2561SHans Petter Selasky const char * 1341c61f2561SHans Petter Selasky libusb20_strerror(int code) 1342c61f2561SHans Petter Selasky { 1343c61f2561SHans Petter Selasky switch (code) { 1344c61f2561SHans Petter Selasky case LIBUSB20_SUCCESS: 1345c61f2561SHans Petter Selasky return ("Success"); 1346c61f2561SHans Petter Selasky case LIBUSB20_ERROR_IO: 1347c61f2561SHans Petter Selasky return ("I/O error"); 1348c61f2561SHans Petter Selasky case LIBUSB20_ERROR_INVALID_PARAM: 1349c61f2561SHans Petter Selasky return ("Invalid parameter"); 1350c61f2561SHans Petter Selasky case LIBUSB20_ERROR_ACCESS: 1351c61f2561SHans Petter Selasky return ("Permissions error"); 1352c61f2561SHans Petter Selasky case LIBUSB20_ERROR_NO_DEVICE: 1353c61f2561SHans Petter Selasky return ("No device"); 1354c61f2561SHans Petter Selasky case LIBUSB20_ERROR_NOT_FOUND: 1355c61f2561SHans Petter Selasky return ("Not found"); 1356c61f2561SHans Petter Selasky case LIBUSB20_ERROR_BUSY: 1357c61f2561SHans Petter Selasky return ("Device busy"); 1358c61f2561SHans Petter Selasky case LIBUSB20_ERROR_TIMEOUT: 1359c61f2561SHans Petter Selasky return ("Timeout"); 1360c61f2561SHans Petter Selasky case LIBUSB20_ERROR_OVERFLOW: 1361c61f2561SHans Petter Selasky return ("Overflow"); 1362c61f2561SHans Petter Selasky case LIBUSB20_ERROR_PIPE: 1363c61f2561SHans Petter Selasky return ("Pipe error"); 1364c61f2561SHans Petter Selasky case LIBUSB20_ERROR_INTERRUPTED: 1365c61f2561SHans Petter Selasky return ("Interrupted"); 1366c61f2561SHans Petter Selasky case LIBUSB20_ERROR_NO_MEM: 1367c61f2561SHans Petter Selasky return ("Out of memory"); 1368c61f2561SHans Petter Selasky case LIBUSB20_ERROR_NOT_SUPPORTED: 1369c61f2561SHans Petter Selasky return ("Not supported"); 1370c61f2561SHans Petter Selasky case LIBUSB20_ERROR_OTHER: 1371c61f2561SHans Petter Selasky return ("Other error"); 1372c61f2561SHans Petter Selasky default: 1373c61f2561SHans Petter Selasky return ("Unknown error"); 1374c61f2561SHans Petter Selasky } 1375c61f2561SHans Petter Selasky } 1376c61f2561SHans Petter Selasky 1377c61f2561SHans Petter Selasky const char * 1378c61f2561SHans Petter Selasky libusb20_error_name(int code) 1379c61f2561SHans Petter Selasky { 1380c61f2561SHans Petter Selasky switch (code) { 1381c61f2561SHans Petter Selasky case LIBUSB20_SUCCESS: 1382c61f2561SHans Petter Selasky return ("LIBUSB20_SUCCESS"); 1383c61f2561SHans Petter Selasky case LIBUSB20_ERROR_IO: 1384c61f2561SHans Petter Selasky return ("LIBUSB20_ERROR_IO"); 1385c61f2561SHans Petter Selasky case LIBUSB20_ERROR_INVALID_PARAM: 1386c61f2561SHans Petter Selasky return ("LIBUSB20_ERROR_INVALID_PARAM"); 1387c61f2561SHans Petter Selasky case LIBUSB20_ERROR_ACCESS: 1388c61f2561SHans Petter Selasky return ("LIBUSB20_ERROR_ACCESS"); 1389c61f2561SHans Petter Selasky case LIBUSB20_ERROR_NO_DEVICE: 1390c61f2561SHans Petter Selasky return ("LIBUSB20_ERROR_NO_DEVICE"); 1391c61f2561SHans Petter Selasky case LIBUSB20_ERROR_NOT_FOUND: 1392c61f2561SHans Petter Selasky return ("LIBUSB20_ERROR_NOT_FOUND"); 1393c61f2561SHans Petter Selasky case LIBUSB20_ERROR_BUSY: 1394c61f2561SHans Petter Selasky return ("LIBUSB20_ERROR_BUSY"); 1395c61f2561SHans Petter Selasky case LIBUSB20_ERROR_TIMEOUT: 1396c61f2561SHans Petter Selasky return ("LIBUSB20_ERROR_TIMEOUT"); 1397c61f2561SHans Petter Selasky case LIBUSB20_ERROR_OVERFLOW: 1398c61f2561SHans Petter Selasky return ("LIBUSB20_ERROR_OVERFLOW"); 1399c61f2561SHans Petter Selasky case LIBUSB20_ERROR_PIPE: 1400c61f2561SHans Petter Selasky return ("LIBUSB20_ERROR_PIPE"); 1401c61f2561SHans Petter Selasky case LIBUSB20_ERROR_INTERRUPTED: 1402c61f2561SHans Petter Selasky return ("LIBUSB20_ERROR_INTERRUPTED"); 1403c61f2561SHans Petter Selasky case LIBUSB20_ERROR_NO_MEM: 1404c61f2561SHans Petter Selasky return ("LIBUSB20_ERROR_NO_MEM"); 1405c61f2561SHans Petter Selasky case LIBUSB20_ERROR_NOT_SUPPORTED: 1406c61f2561SHans Petter Selasky return ("LIBUSB20_ERROR_NOT_SUPPORTED"); 1407c61f2561SHans Petter Selasky case LIBUSB20_ERROR_OTHER: 1408c61f2561SHans Petter Selasky return ("LIBUSB20_ERROR_OTHER"); 1409c61f2561SHans Petter Selasky default: 1410c61f2561SHans Petter Selasky return ("LIBUSB20_ERROR_UNKNOWN"); 1411c61f2561SHans Petter Selasky } 1412c61f2561SHans Petter Selasky } 1413