summaryrefslogtreecommitdiff
path: root/Radio/HW/BladeRF/src/backend/usb/libusb.c
diff options
context:
space:
mode:
Diffstat (limited to 'Radio/HW/BladeRF/src/backend/usb/libusb.c')
-rw-r--r--Radio/HW/BladeRF/src/backend/usb/libusb.c1504
1 files changed, 1504 insertions, 0 deletions
diff --git a/Radio/HW/BladeRF/src/backend/usb/libusb.c b/Radio/HW/BladeRF/src/backend/usb/libusb.c
new file mode 100644
index 0000000..1f63bbc
--- /dev/null
+++ b/Radio/HW/BladeRF/src/backend/usb/libusb.c
@@ -0,0 +1,1504 @@
+/*
+ * This file is part of the bladeRF project:
+ * http://www.github.com/nuand/bladeRF
+ *
+ * Copyright (C) 2013-2016 Nuand LLC
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include "host_config.h"
+#include <stdlib.h>
+#include <string.h>
+#include <pthread.h>
+#include <errno.h>
+#include "libusb.h"
+#if 1 == BLADERF_OS_FREEBSD
+#include <limits.h>
+#endif // BLADERF_OS_FREEBSD
+
+#include "log.h"
+
+#include "devinfo.h"
+#include "backend/backend.h"
+#include "backend/usb/usb.h"
+#include "streaming/async.h"
+#include "helpers/timeout.h"
+
+#include "bladeRF.h"
+
+#include "host_config.h"
+
+#ifndef LIBUSB_HANDLE_EVENTS_TIMEOUT_NSEC
+# define LIBUSB_HANDLE_EVENTS_TIMEOUT_NSEC (15 * 1000)
+#endif
+
+struct bladerf_lusb {
+ libusb_device *dev;
+ libusb_device_handle *handle;
+ libusb_context *context;
+#if 1 == BLADERF_OS_WINDOWS
+ HANDLE mutex;
+#endif // BLADERF_OS_WINDOWS
+};
+
+typedef enum {
+ TRANSFER_UNINITIALIZED = 0,
+ TRANSFER_AVAIL,
+ TRANSFER_IN_FLIGHT,
+ TRANSFER_CANCEL_PENDING
+} transfer_status;
+
+struct lusb_stream_data {
+ size_t num_transfers; /* Total # of allocated transfers */
+ size_t num_avail; /* # of currently available transfers */
+ size_t i; /* Index to next transfer */
+ struct libusb_transfer **transfers; /* Array of transfer metadata */
+ transfer_status *transfer_status; /* Status of each transfer */
+
+ /* Warn the first time we get a transfer callback out of order.
+ * This shouldn't happen normally, but we've seen it intermittently on
+ * libusb 1.0.19 for Windows. Further investigation required...
+ */
+ bool out_of_order_event;
+};
+
+static inline struct bladerf_lusb * lusb_backend(struct bladerf *dev)
+{
+ struct bladerf_usb *usb;
+
+ assert(dev && dev->backend_data);
+ usb = (struct bladerf_usb *) dev->backend_data;
+
+ assert(usb->driver);
+ return (struct bladerf_lusb *) usb->driver;
+}
+
+/* Convert libusb error codes to libbladeRF error codes */
+static int error_conv(int error)
+{
+ int ret;
+
+ switch (error) {
+ case 0:
+ ret = 0;
+ break;
+
+ case LIBUSB_ERROR_IO:
+ ret = BLADERF_ERR_IO;
+ break;
+
+ case LIBUSB_ERROR_INVALID_PARAM:
+ ret = BLADERF_ERR_INVAL;
+ break;
+
+ case LIBUSB_ERROR_BUSY:
+ case LIBUSB_ERROR_NO_DEVICE:
+ ret = BLADERF_ERR_NODEV;
+ break;
+
+ case LIBUSB_ERROR_TIMEOUT:
+ ret = BLADERF_ERR_TIMEOUT;
+ break;
+
+ case LIBUSB_ERROR_NO_MEM:
+ ret = BLADERF_ERR_MEM;
+ break;
+
+ case LIBUSB_ERROR_NOT_SUPPORTED:
+ ret = BLADERF_ERR_UNSUPPORTED;
+ break;
+
+ case LIBUSB_ERROR_ACCESS:
+ ret = BLADERF_ERR_PERMISSION;
+ break;
+
+ case LIBUSB_ERROR_OVERFLOW:
+ case LIBUSB_ERROR_PIPE:
+ case LIBUSB_ERROR_INTERRUPTED:
+ case LIBUSB_ERROR_NOT_FOUND:
+ default:
+ ret = BLADERF_ERR_UNEXPECTED;
+ }
+
+ return ret;
+}
+
+/* Returns libusb error codes */
+static int get_devinfo(libusb_device *dev, struct bladerf_devinfo *info)
+{
+ int status = 0;
+ libusb_device_handle *handle;
+ struct libusb_device_descriptor desc;
+
+ /* Populate device info */
+ bladerf_init_devinfo(info);
+ info->backend = BLADERF_BACKEND_LIBUSB;
+ info->usb_bus = libusb_get_bus_number(dev);
+ info->usb_addr = libusb_get_device_address(dev);
+
+ status = libusb_open(dev, &handle);
+
+ if (status == 0) {
+ status = libusb_get_device_descriptor(dev, &desc);
+ if (status != 0) {
+ memset(info->serial, 0, BLADERF_SERIAL_LENGTH);
+ } else {
+ status = libusb_get_string_descriptor_ascii(
+ handle, desc.iSerialNumber, (unsigned char *)&info->serial,
+ BLADERF_SERIAL_LENGTH);
+
+ /* Consider this to be non-fatal, otherwise firmware <= 1.1
+ * wouldn't be able to get far enough to upgrade */
+ if (status < 0) {
+ log_debug("Failed to retrieve serial number\n");
+ memset(info->serial, 0, BLADERF_SERIAL_LENGTH);
+ }
+
+ status = libusb_get_string_descriptor_ascii(
+ handle, desc.iManufacturer,
+ (unsigned char *)&info->manufacturer,
+ BLADERF_DESCRIPTION_LENGTH);
+
+ if (status < 0) {
+ log_debug("Failed to retrieve manufacturer string\n");
+ memset(info->manufacturer, 0, BLADERF_DESCRIPTION_LENGTH);
+ }
+
+ status = libusb_get_string_descriptor_ascii(
+ handle, desc.iProduct, (unsigned char *)&info->product,
+ BLADERF_DESCRIPTION_LENGTH);
+
+ if (status < 0) {
+ log_debug("Failed to retrieve product string\n");
+ memset(info->product, 0, BLADERF_DESCRIPTION_LENGTH);
+ }
+
+ log_debug("Bus %03d Device %03d: %s %s, serial %s\n", info->usb_bus,
+ info->usb_addr, info->manufacturer, info->product,
+ info->serial);
+
+ if (status > 0) {
+ status = 0;
+ }
+ }
+
+ libusb_close(handle);
+ }
+
+ return status;
+}
+
+static bool device_has_vid_pid(libusb_device *dev, uint16_t vid, uint16_t pid)
+{
+ int status;
+ struct libusb_device_descriptor desc;
+ bool match = false;
+
+ status = libusb_get_device_descriptor(dev, &desc);
+ if (status != 0) {
+ log_debug("Couldn't get device descriptor: %s\n",
+ libusb_error_name(status));
+ } else {
+ match = (desc.idVendor == vid) && (desc.idProduct == pid);
+ }
+
+ return match;
+}
+
+static bool device_is_fx3_bootloader(libusb_device *dev)
+{
+ return device_has_vid_pid(dev, USB_CYPRESS_VENDOR_ID, USB_FX3_PRODUCT_ID) ||
+ device_has_vid_pid(dev, USB_NUAND_VENDOR_ID, USB_NUAND_BLADERF_BOOT_PRODUCT_ID) ||
+ device_has_vid_pid(dev, USB_NUAND_LEGACY_VENDOR_ID, USB_NUAND_BLADERF_LEGACY_BOOT_PRODUCT_ID);
+}
+
+static inline bool device_has_bladeRF_ids(libusb_device *dev)
+{
+ return device_has_vid_pid(dev, USB_NUAND_VENDOR_ID, USB_NUAND_BLADERF_PRODUCT_ID) ||
+ device_has_vid_pid(dev, USB_NUAND_VENDOR_ID, USB_NUAND_BLADERF2_PRODUCT_ID) ||
+ device_has_vid_pid(dev, USB_NUAND_LEGACY_VENDOR_ID, USB_NUAND_BLADERF_LEGACY_PRODUCT_ID);
+
+}
+
+static bool device_is_bladerf(libusb_device *dev)
+{
+ struct libusb_config_descriptor *cfg;
+ int status;
+ bool ret;
+
+ if (!device_has_bladeRF_ids(dev)) {
+ return false;
+ }
+
+ status = libusb_get_config_descriptor(dev, 0, &cfg);
+ if (status != 0) {
+ log_debug("Failed to get configuration descriptor: %s\n",
+ libusb_error_name(status));
+ return false;
+ }
+
+ /* As of firmware v0.4, we expect there to be 4 altsettings on the
+ * first interface. */
+ if (cfg->interface->num_altsetting != 4) {
+ const uint8_t bus = libusb_get_bus_number(dev);
+ const uint8_t addr = libusb_get_device_address(dev);
+
+ log_warning("A bladeRF running incompatible firmware appears to be "
+ "present on bus=%u, addr=%u. If this is true, a firmware "
+ "update via the device's bootloader is required.\n\n",
+ bus, addr);
+
+ ret = false;
+ } else {
+ ret = true;
+ }
+
+ libusb_free_config_descriptor(cfg);
+ return ret;
+}
+
+static bool device_is_probe_target(backend_probe_target probe_target,
+ libusb_device *dev)
+{
+ bool is_probe_target = false;
+
+ switch (probe_target) {
+ case BACKEND_PROBE_BLADERF:
+ is_probe_target = device_is_bladerf(dev);
+ if (is_probe_target) {
+ log_verbose("Found a bladeRF\n");
+ }
+ break;
+
+ case BACKEND_PROBE_FX3_BOOTLOADER:
+ is_probe_target = device_is_fx3_bootloader(dev);
+ if (is_probe_target) {
+ log_verbose("Found an FX3 bootloader.\n");
+ }
+ break;
+
+ default:
+ assert(!"Invalid probe target");
+ }
+
+ return is_probe_target;
+}
+
+static int lusb_probe(backend_probe_target probe_target,
+ struct bladerf_devinfo_list *info_list)
+{
+ int status, i, n;
+ ssize_t count;
+ libusb_device **list;
+ struct bladerf_devinfo info;
+ bool printed_access_warning = false;
+
+ libusb_context *context;
+
+ /* Initialize libusb for device tree walking */
+ status = libusb_init(&context);
+ if (status) {
+ log_error("Could not initialize libusb: %s\n",
+ libusb_error_name(status));
+ goto lusb_probe_done;
+ }
+
+ count = libusb_get_device_list(context, &list);
+ /* Iterate through all the USB devices */
+ for (i = 0, n = 0; i < count && status == 0; i++) {
+ if (device_is_probe_target(probe_target, list[i])) {
+ bool do_add = true;
+
+ /* Open the USB device and get some information */
+ status = get_devinfo(list[i], &info);
+ if (status) {
+ /* We may not be able to open the device if another driver
+ * (e.g., CyUSB3) is associated with it. Therefore, just log to
+ * the debug level and carry on. */
+ log_debug("Could not open device: %s\n",
+ libusb_error_name(status));
+
+ if (status == LIBUSB_ERROR_ACCESS) {
+ /* If it's an access error, odds are good this is happening
+ * because we've already got the device open. Pass the info
+ * we have back to the caller. */
+ do_add = true;
+
+ if (!printed_access_warning) {
+ printed_access_warning = true;
+ log_warning(
+ "Found a bladeRF via VID/PID, but could not open "
+ "it due to insufficient permissions, or because "
+ "the device is already open.\n");
+ }
+ } else {
+ do_add = false;
+ }
+
+ /* Don't stop probing because one device was "problematic" */
+ status = 0;
+ }
+
+ if (do_add) {
+ info.instance = n++;
+
+ status = bladerf_devinfo_list_add(info_list, &info);
+ if (status) {
+ log_error("Could not add device to list: %s\n",
+ bladerf_strerror(status));
+ } else {
+ log_verbose("Added instance %d to device list\n",
+ info.instance);
+ }
+ }
+ }
+ }
+
+ libusb_free_device_list(list, 1);
+ libusb_exit(context);
+
+lusb_probe_done:
+ return status;
+}
+
+#ifdef HAVE_LIBUSB_GET_VERSION
+static inline void get_libusb_version(char *buf, size_t buf_len)
+{
+ const struct libusb_version *version;
+ version = libusb_get_version();
+
+ snprintf(buf, buf_len, "%d.%d.%d.%d%s", version->major, version->minor,
+ version->micro, version->nano, version->rc);
+}
+#else
+static void get_libusb_version(char *buf, size_t buf_len)
+{
+ snprintf(buf, buf_len, "<= 1.0.9");
+}
+#endif
+
+static int create_device_mutex(const struct bladerf_devinfo *info,
+ struct bladerf_lusb *dev)
+{
+ int status = 0;
+
+#if 1 == BLADERF_OS_WINDOWS
+ const char prefix[] = "Global\\bladeRF-";
+ const size_t mutex_name_len = strlen(prefix) + BLADERF_SERIAL_LENGTH;
+ char *mutex_name = (char *)calloc(mutex_name_len, 1);
+ DWORD last_error;
+
+ if (NULL == mutex_name) {
+ return BLADERF_ERR_MEM;
+ }
+
+ snprintf(mutex_name, mutex_name_len, "%s%s", prefix, info->serial);
+ log_verbose("Mutex name: %s\n", mutex_name);
+
+ SetLastError(0);
+ dev->mutex = CreateMutex(NULL, true, mutex_name);
+ last_error = GetLastError();
+ if (NULL == dev->mutex || last_error != 0) {
+ log_debug("Unable to create device mutex: mutex=%p, last_error=%ld\n",
+ dev->mutex, last_error);
+ status = BLADERF_ERR_NODEV;
+ ReleaseMutex(dev->mutex);
+ CloseHandle(dev->mutex);
+ }
+
+ free(mutex_name);
+#endif // BLADERF_OS_WINDOWS
+
+ return status;
+}
+
+static int open_device(const struct bladerf_devinfo *info,
+ libusb_context *context,
+ libusb_device *libusb_dev_in,
+ struct bladerf_lusb **dev_out)
+{
+ int status;
+ struct bladerf_lusb *dev;
+
+ *dev_out = NULL;
+
+ dev = (struct bladerf_lusb *) calloc(1, sizeof(dev[0]));
+ if (dev == NULL) {
+ log_debug("Failed to allocate handle for instance %d.\n",
+ info->instance);
+
+ /* Report "no device" so we could try again with
+ * another matching device */
+ return BLADERF_ERR_NODEV;
+ }
+
+ dev->context = context;
+ dev->dev = libusb_dev_in;
+
+ status = libusb_open(libusb_dev_in, &dev->handle);
+ if (status < 0) {
+ log_debug("Failed to open device instance %d: %s\n",
+ info->instance, libusb_error_name(status));
+
+ status = error_conv(status);
+ goto out;
+ }
+
+ status = libusb_claim_interface(dev->handle, 0);
+ if (status < 0) {
+ log_debug("Failed to claim interface 0 for instance %d: %s\n",
+ info->instance, libusb_error_name(status));
+
+ status = error_conv(status);
+ goto out;
+ }
+
+ status = create_device_mutex(info, dev);
+ if (status < 0) {
+ log_debug("Failed to get device mutex for instance %d: %s\n",
+ info->instance, bladerf_strerror(status));
+
+ status = error_conv(status);
+ goto out;
+ }
+
+out:
+ if (status != 0) {
+ if (dev->handle != NULL) {
+ libusb_close(dev->handle);
+ }
+
+ free(dev);
+ } else {
+ *dev_out = dev;
+ }
+
+ return status;
+}
+
+static int find_and_open_device(libusb_context *context,
+ const struct bladerf_devinfo *info_in,
+ struct bladerf_lusb **dev_out,
+ struct bladerf_devinfo *info_out)
+{
+ int status = BLADERF_ERR_NODEV;
+ int i, n;
+ ssize_t count;
+ struct libusb_device **list;
+ struct bladerf_devinfo curr_info;
+ bool printed_access_warning = false;
+
+ *dev_out = NULL;
+
+ count = libusb_get_device_list(context, &list);
+ if (count < 0) {
+ if (count < INT_MIN) {
+ /* Ensure we don't have a situation where we accidentally return 0
+ * due to a narrowing conversion */
+ return BLADERF_ERR_UNEXPECTED;
+ } else {
+ return error_conv((int) count);
+ }
+ }
+
+ for (i = 0, n = 0; (i < count) && (*dev_out == NULL); i++) {
+ if (device_is_bladerf(list[i])) {
+ log_verbose("Found a bladeRF (idx=%d)\n", i);
+
+ /* Open the USB device and get some information */
+ status = get_devinfo(list[i], &curr_info);
+ if (status < 0) {
+
+ /* Give the user a helpful hint in case the have forgotten
+ * to update their udev rules */
+ if (status == LIBUSB_ERROR_ACCESS && !printed_access_warning) {
+ printed_access_warning = true;
+ log_warning("Found a bladeRF via VID/PID, but could not "
+ "open it due to insufficient permissions.\n");
+ } else {
+ log_debug("Could not open bladeRF device: %s\n",
+ libusb_error_name(status) );
+ }
+
+ status = BLADERF_ERR_NODEV;
+ continue; /* Continue trying the next devices */
+ } else {
+ curr_info.instance = n++;
+ }
+
+ /* Check to see if this matches the info struct */
+ if (bladerf_devinfo_matches(&curr_info, info_in)) {
+ status = open_device(&curr_info, context, list[i], dev_out);
+ if (status < 0) {
+ status = BLADERF_ERR_NODEV;
+ continue; /* Continue trying the next matching device */
+ } else {
+ memcpy(info_out, &curr_info, sizeof(info_out[0]));
+ }
+ } else {
+ status = BLADERF_ERR_NODEV;
+
+ log_verbose("Devinfo doesn't match - skipping"
+ "(instance=%d, serial=%d, bus/addr=%d\n",
+ bladerf_instance_matches(&curr_info, info_in),
+ bladerf_serial_matches(&curr_info, info_in),
+ bladerf_bus_addr_matches(&curr_info, info_in));
+ }
+ }
+ }
+
+ if (status == 0) {
+ /* Returning 0 indicates this function is providing a device */
+ assert(*dev_out != NULL);
+ }
+
+ libusb_free_device_list(list, 1);
+ return status;
+}
+
+#if ENABLE_USB_DEV_RESET_ON_OPEN
+static int reset_and_reopen(libusb_context *context,
+ struct bladerf_lusb **dev,
+ struct bladerf_devinfo *info)
+{
+ int status;
+
+ status = libusb_reset_device((*dev)->handle);
+ if (status == 0) {
+ log_verbose("USB port reset succeeded for bladeRF %s\n", info->serial);
+ return 0;
+ } else if (status == LIBUSB_ERROR_NO_DEVICE) {
+ struct bladerf_devinfo new_info;
+
+ /* The reset has caused the device to drop out and re-enumerate.
+ *
+ * We'll find it again via the info we gathered about it via its
+ * serial number, which is now stored in the devinfo
+ */
+ log_verbose("Re-scan required after port reset for bladeRF %s\n",
+ info->serial);
+
+
+ libusb_release_interface((*dev)->handle, 0);
+ libusb_close((*dev)->handle);
+#if 1 == BLADERF_OS_WINDOWS
+ ReleaseMutex((*dev)->mutex);
+ CloseHandle((*dev)->mutex);
+#endif // BLADERF_OS_WINDOWS
+
+ *dev = NULL;
+
+ memcpy(&new_info, info, sizeof(new_info));
+ new_info.usb_bus = DEVINFO_BUS_ANY;
+ new_info.usb_addr = DEVINFO_ADDR_ANY;
+
+ status = find_and_open_device(context, &new_info, dev, info);
+
+ } else {
+ status = BLADERF_ERR_IO;
+ log_verbose("Port reset failed for bladerf %s: %s\n",
+ info->serial, libusb_error_name(status));
+ }
+
+ return status;
+}
+#endif
+
+static int lusb_open(void **driver,
+ struct bladerf_devinfo *info_in,
+ struct bladerf_devinfo *info_out)
+{
+ int status;
+ struct bladerf_lusb *lusb = NULL;
+ libusb_context *context;
+
+ /* Initialize libusb for device tree walking */
+ status = libusb_init(&context);
+ if (status) {
+ log_error("Could not initialize libusb: %s\n",
+ libusb_error_name(status));
+ return error_conv(status);
+ }
+
+ /* We can only print this out when log output is enabled, or else we'll
+ * get snagged by -Werror=unused-but-set-variable */
+# ifdef LOGGING_ENABLED
+ {
+ char buf[64];
+ get_libusb_version(buf, sizeof(buf));
+ log_verbose("Using libusb version: %s\n", buf);
+ }
+# endif
+
+ status = find_and_open_device(context, info_in, &lusb, info_out);
+ if (status != 0) {
+ libusb_exit(context);
+
+ if (status == BLADERF_ERR_NODEV) {
+ log_debug("No devices available on the libusb backend.\n");
+ } else {
+ log_debug("Failed to open bladeRF on libusb backend: %s\n",
+ bladerf_strerror(status));
+ }
+ } else {
+ assert(lusb != NULL);
+
+ /* Cosmin and Marian from Null Team (null.ro) and YateBTS(.com) found
+ * that it is possible to recover from "Issue #95: Not enough bandwidth
+ * for altsetting" by performing a USB port reset prior to actually
+ * trying to use the device.
+ */
+# if ENABLE_USB_DEV_RESET_ON_OPEN
+ if (bladerf_usb_reset_device_on_open) {
+ status = reset_and_reopen(context, &lusb, info_out);
+ }
+# endif
+
+ if (status == 0) {
+ *driver = (void *) lusb;
+ }
+ }
+
+ return status;
+}
+
+static int lusb_change_setting(void *driver, uint8_t setting)
+{
+ struct bladerf_lusb *lusb = (struct bladerf_lusb *) driver;
+
+ int status = libusb_set_interface_alt_setting(lusb->handle, 0, setting);
+
+ return error_conv(status);
+}
+
+static void lusb_close(void *driver)
+{
+ int status;
+ struct bladerf_lusb *lusb = (struct bladerf_lusb *) driver;
+
+ status = libusb_release_interface(lusb->handle, 0);
+ if (status < 0) {
+ log_error("Failed to release interface: %s\n",
+ libusb_error_name(status));
+ }
+
+ libusb_close(lusb->handle);
+ libusb_exit(lusb->context);
+#if 1 == BLADERF_OS_WINDOWS
+ ReleaseMutex(lusb->mutex);
+ CloseHandle(lusb->mutex);
+#endif // BLADERF_OS_WINDOWS
+ free(lusb);
+}
+
+static void lusb_close_bootloader(void *driver)
+{
+ int status;
+ struct bladerf_lusb *lusb = (struct bladerf_lusb *) driver;
+
+ if (lusb != NULL) {
+ if (lusb->handle != NULL) {
+ status = libusb_release_interface(lusb->handle, 0);
+ if (status < 0) {
+ log_debug("Failed to release interface: %s\n",
+ libusb_error_name(status));
+ }
+
+ libusb_close(lusb->handle);
+ }
+
+ if (lusb->context != NULL) {
+ libusb_exit(lusb->context);
+ }
+
+#if 1 == BLADERF_OS_WINDOWS
+ ReleaseMutex(lusb->mutex);
+ CloseHandle(lusb->mutex);
+#endif // BLADERF_OS_WINDOWS
+
+ free(lusb);
+ }
+}
+
+static inline bool bus_matches(uint8_t bus, struct libusb_device *d)
+{
+ return (bus == DEVINFO_BUS_ANY) ||
+ (bus == libusb_get_bus_number(d));
+}
+
+static inline bool addr_matches(uint8_t addr, struct libusb_device *d)
+{
+ return (addr == DEVINFO_BUS_ANY) ||
+ (addr == libusb_get_device_address(d));
+}
+
+static int lusb_open_bootloader(void **driver, uint8_t bus, uint8_t addr)
+{
+ int status;
+ struct libusb_device **dev_list = NULL;
+ ssize_t dev_list_size, i;
+ struct bladerf_lusb *lusb;
+
+ *driver = NULL;
+
+ lusb = calloc(1, sizeof(lusb[0]));
+ if (lusb == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ status = libusb_init(&lusb->context);
+ if (status != 0) {
+ log_debug("Failed to initialize libusb context: %s\n",
+ libusb_error_name(status));
+ goto error;
+ }
+
+ dev_list_size = libusb_get_device_list(lusb->context, &dev_list);
+ if (dev_list_size < 0) {
+ log_debug("Failed to get device list: %s\n", libusb_error_name(status));
+ status = (int) dev_list_size;
+ goto error;
+ }
+
+ for (i = 0; i < dev_list_size; i++) {
+ if (device_is_fx3_bootloader(dev_list[i]) &&
+ bus_matches(bus, dev_list[i]) &&
+ addr_matches(addr, dev_list[i])) {
+
+
+ status = libusb_open(dev_list[i], &lusb->handle);
+ if (status != 0) {
+ log_debug("Failed to open device: %s\n",
+ libusb_error_name(status));
+ goto error;
+ } else {
+ status = libusb_claim_interface(lusb->handle, 0);
+ if (status < 0) {
+ log_debug("Failed to claim interface: %s\n",
+ libusb_error_name(status));
+
+ goto error;
+ } else {
+ log_verbose("Opened bootloader at %u:%u\n",
+ libusb_get_bus_number(dev_list[i]),
+ libusb_get_device_address(dev_list[i]));
+ *driver = lusb;
+ }
+ break;
+ }
+ }
+ }
+
+error:
+ if (dev_list != NULL) {
+ libusb_free_device_list(dev_list, 1);
+ }
+
+ if (status != 0) {
+ status = error_conv(status);
+ lusb_close_bootloader(lusb);
+ } else if (*driver == NULL) {
+ status = BLADERF_ERR_NODEV;
+ lusb_close_bootloader(lusb);
+ }
+
+ return status;
+}
+
+static int lusb_get_vid_pid(void *driver, uint16_t *vid,
+ uint16_t *pid)
+{
+ struct bladerf_lusb *lusb = (struct bladerf_lusb *)driver;
+ struct libusb_device_descriptor desc;
+ int status;
+
+ status = libusb_get_device_descriptor(lusb->dev, &desc);
+ if (status != 0) {
+ log_debug("Couldn't get device descriptor: %s\n",
+ libusb_error_name(status));
+ return BLADERF_ERR_IO;
+ }
+
+ *vid = desc.idVendor;
+ *pid = desc.idProduct;
+
+ return 0;
+}
+
+static int lusb_get_handle(void *driver, void **handle)
+{
+ struct bladerf_lusb *lusb = (struct bladerf_lusb *) driver;
+ *handle = lusb->handle;
+
+ return 0;
+}
+static int lusb_get_speed(void *driver,
+ bladerf_dev_speed *device_speed)
+{
+ int speed;
+ int status = 0;
+ struct bladerf_lusb *lusb = (struct bladerf_lusb *) driver;
+
+ speed = libusb_get_device_speed(lusb->dev);
+ if (speed == LIBUSB_SPEED_SUPER) {
+ *device_speed = BLADERF_DEVICE_SPEED_SUPER;
+ } else if (speed == LIBUSB_SPEED_HIGH) {
+ *device_speed = BLADERF_DEVICE_SPEED_HIGH;
+ } else {
+ *device_speed = BLADERF_DEVICE_SPEED_UNKNOWN;
+
+ if (speed == LIBUSB_SPEED_FULL) {
+ log_debug("Full speed connection is not suppored.\n");
+ status = BLADERF_ERR_UNSUPPORTED;
+ } else if (speed == LIBUSB_SPEED_LOW) {
+ log_debug("Low speed connection is not supported.\n");
+ status = BLADERF_ERR_UNSUPPORTED;
+ } else {
+ log_debug("Unknown/unexpected device speed (%d)\n", speed);
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+ }
+
+ return status;
+}
+
+static inline uint8_t bm_request_type(usb_target target_type,
+ usb_request req_type,
+ usb_direction direction)
+{
+ uint8_t ret = 0;
+
+ switch (target_type) {
+ case USB_TARGET_DEVICE:
+ ret |= LIBUSB_RECIPIENT_DEVICE;
+ break;
+
+ case USB_TARGET_INTERFACE:
+ ret |= LIBUSB_RECIPIENT_INTERFACE;
+ break;
+
+ case USB_TARGET_ENDPOINT:
+ ret |= LIBUSB_RECIPIENT_ENDPOINT;
+ break;
+
+ default:
+ ret |= LIBUSB_RECIPIENT_OTHER;
+
+ }
+
+ switch (req_type) {
+ case USB_REQUEST_STANDARD:
+ ret |= LIBUSB_REQUEST_TYPE_STANDARD;
+ break;
+
+ case USB_REQUEST_CLASS:
+ ret |= LIBUSB_REQUEST_TYPE_CLASS;
+ break;
+
+ case USB_REQUEST_VENDOR:
+ ret |= LIBUSB_REQUEST_TYPE_VENDOR;
+ break;
+ }
+
+ switch (direction) {
+ case USB_DIR_HOST_TO_DEVICE:
+ ret |= LIBUSB_ENDPOINT_OUT;
+ break;
+
+ case USB_DIR_DEVICE_TO_HOST:
+ ret |= LIBUSB_ENDPOINT_IN;
+ break;
+ }
+
+ return ret;
+}
+
+static int lusb_control_transfer(void *driver,
+ usb_target target_type, usb_request req_type,
+ usb_direction dir, uint8_t request,
+ uint16_t wvalue, uint16_t windex,
+ void *buffer, uint32_t buffer_len,
+ uint32_t timeout_ms)
+{
+
+ int status;
+ struct bladerf_lusb *lusb = (struct bladerf_lusb *) driver;
+ const uint8_t bm_req_type = bm_request_type(target_type, req_type, dir);
+
+ status = libusb_control_transfer(lusb->handle,
+ bm_req_type, request,
+ wvalue, windex,
+ buffer, buffer_len,
+ timeout_ms);
+
+ if (status >= 0 && (uint32_t)status == buffer_len) {
+ status = 0;
+ } else {
+ log_debug("%s failed: status = %d\n", __FUNCTION__, status);
+ }
+
+ return error_conv(status);
+}
+
+static int lusb_bulk_transfer(void *driver, uint8_t endpoint, void *buffer,
+ uint32_t buffer_len, uint32_t timeout_ms)
+{
+ int status;
+ int n_transferred;
+ struct bladerf_lusb *lusb = (struct bladerf_lusb *) driver;
+
+ status = libusb_bulk_transfer(lusb->handle, endpoint, buffer, buffer_len,
+ &n_transferred, timeout_ms);
+
+ status = error_conv(status);
+ if (status == 0 && ((uint32_t)n_transferred != buffer_len)) {
+ log_debug("Short bulk transfer: requested=%u, transferred=%u\n",
+ buffer_len, n_transferred);
+ status = BLADERF_ERR_IO;
+ }
+
+ return status;
+}
+
+static int lusb_get_string_descriptor(void *driver, uint8_t index,
+ void *buffer, uint32_t buffer_len)
+{
+ int status;
+ struct bladerf_lusb *lusb = (struct bladerf_lusb *) driver;
+
+ status = libusb_get_string_descriptor_ascii(lusb->handle, index,
+ (unsigned char*)buffer,
+ buffer_len);
+
+ if (status > 0 && (uint32_t)status < buffer_len) {
+ status = 0;
+ } else {
+ status = BLADERF_ERR_UNEXPECTED;
+ }
+
+ return status;
+}
+
+/* At the risk of being a little inefficient, just keep attempting to cancel
+ * everything. If a transfer's no longer active, we'll just get a NOT_FOUND
+ * error -- no big deal. Just accepting that alleviates the need to track
+ * the status of each transfer...
+ */
+static inline void cancel_all_transfers(struct bladerf_stream *stream)
+{
+ size_t i;
+ int status;
+ struct lusb_stream_data *stream_data = stream->backend_data;
+
+ for (i = 0; i < stream_data->num_transfers; i++) {
+ if (stream_data->transfer_status[i] == TRANSFER_IN_FLIGHT) {
+ status = libusb_cancel_transfer(stream_data->transfers[i]);
+ if (status < 0 && status != LIBUSB_ERROR_NOT_FOUND) {
+ log_error("Error canceling transfer (%d): %s\n",
+ status, libusb_error_name(status));
+ } else {
+ stream_data->transfer_status[i] = TRANSFER_CANCEL_PENDING;
+ }
+ }
+ }
+}
+
+static inline size_t transfer_idx(struct lusb_stream_data *stream_data,
+ struct libusb_transfer *transfer)
+{
+ size_t i;
+ for (i = 0; i < stream_data->num_transfers; i++) {
+ if (stream_data->transfers[i] == transfer) {
+ return i;
+ }
+ }
+
+ return UINT_MAX;
+}
+
+static int submit_transfer(struct bladerf_stream *stream, void *buffer, size_t len);
+
+static void LIBUSB_CALL lusb_stream_cb(struct libusb_transfer *transfer)
+{
+ struct bladerf_stream *stream = transfer->user_data;
+ void *next_buffer = NULL;
+ struct bladerf_metadata metadata;
+ struct lusb_stream_data *stream_data = stream->backend_data;
+ size_t transfer_i;
+
+ /* Currently unused - zero out for out own debugging sanity... */
+ memset(&metadata, 0, sizeof(metadata));
+
+ MUTEX_LOCK(&stream->lock);
+
+ transfer_i = transfer_idx(stream_data, transfer);
+ assert(stream_data->transfer_status[transfer_i] == TRANSFER_IN_FLIGHT ||
+ stream_data->transfer_status[transfer_i] == TRANSFER_CANCEL_PENDING);
+
+ if (transfer_i >= stream_data->num_transfers) {
+ log_error("Unable to find transfer\n");
+ stream->state = STREAM_SHUTTING_DOWN;
+ } else {
+ stream_data->transfer_status[transfer_i] = TRANSFER_AVAIL;
+ stream_data->num_avail++;
+ pthread_cond_signal(&stream->can_submit_buffer);
+ }
+
+ /* Check to see if the transfer has been cancelled or errored */
+ if (transfer->status != LIBUSB_TRANSFER_COMPLETED) {
+ /* Errored out for some reason .. */
+ stream->state = STREAM_SHUTTING_DOWN;
+
+ switch (transfer->status) {
+ case LIBUSB_TRANSFER_CANCELLED:
+ /* We expect this case when we begin tearing down the stream */
+ break;
+
+ case LIBUSB_TRANSFER_STALL:
+ log_error("Hit stall for buffer %p\n\r", transfer->buffer);
+ stream->error_code = BLADERF_ERR_IO;
+ break;
+
+ case LIBUSB_TRANSFER_ERROR:
+ log_error("Got transfer error for buffer %p\n\r",
+ transfer->buffer);
+ stream->error_code = BLADERF_ERR_IO;
+ break;
+
+ case LIBUSB_TRANSFER_OVERFLOW:
+ log_error("Got transfer over for buffer %p, "
+ "transfer \"actual_length\" = %d\n\r",
+ transfer->buffer, transfer->actual_length);
+ stream->error_code = BLADERF_ERR_IO;
+ break;
+
+ case LIBUSB_TRANSFER_TIMED_OUT:
+ log_error("Transfer timed out for buffer %p\n\r",
+ transfer->buffer);
+ stream->error_code = BLADERF_ERR_TIMEOUT;
+ break;
+
+ case LIBUSB_TRANSFER_NO_DEVICE:
+ stream->error_code = BLADERF_ERR_NODEV;
+ break;
+
+ default:
+ log_error("Unexpected transfer status: %d\n\r", transfer->status);
+ break;
+ }
+ }
+
+ if (stream->state == STREAM_RUNNING) {
+ if (stream->format == BLADERF_FORMAT_PACKET_META) {
+ /* Call user callback requesting more data to transmit */
+ next_buffer = stream->cb(
+ stream->dev, stream, &metadata, transfer->buffer,
+ bytes_to_samples(stream->format, transfer->actual_length), stream->user_data);
+ } else {
+ /* Sanity check for debugging purposes */
+ if (transfer->length != transfer->actual_length) {
+ log_warning("Received short transfer\n");
+ }
+
+ /* Call user callback requesting more data to transmit */
+ next_buffer = stream->cb(
+ stream->dev, stream, &metadata, transfer->buffer,
+ bytes_to_samples(stream->format, transfer->actual_length), stream->user_data);
+ }
+
+ if (next_buffer == BLADERF_STREAM_SHUTDOWN) {
+ stream->state = STREAM_SHUTTING_DOWN;
+ } else if (next_buffer != BLADERF_STREAM_NO_DATA) {
+ int status;
+ if((stream->layout & BLADERF_DIRECTION_MASK) == BLADERF_TX
+ && stream->format == BLADERF_FORMAT_PACKET_META) {
+ status = submit_transfer(stream, next_buffer, metadata.actual_count);
+ } else {
+ status = submit_transfer(stream, next_buffer, async_stream_buf_bytes(stream));
+ }
+ if (status != 0) {
+ /* If this fails, we probably have a serious problem...so just
+ * shut it down. */
+ stream->state = STREAM_SHUTTING_DOWN;
+ }
+ }
+ }
+
+
+ /* Check to see if all the transfers have been cancelled,
+ * and if so, clean up the stream */
+ if (stream->state == STREAM_SHUTTING_DOWN) {
+ /* We know we're done when all of our transfers have returned to their
+ * "available" states */
+ if (stream_data->num_avail == stream_data->num_transfers) {
+ stream->state = STREAM_DONE;
+ } else {
+ cancel_all_transfers(stream);
+ }
+ }
+
+ MUTEX_UNLOCK(&stream->lock);
+}
+
+static inline struct libusb_transfer *
+get_next_available_transfer(struct lusb_stream_data *stream_data)
+{
+ unsigned int n;
+ size_t i = stream_data->i;
+
+ for (n = 0; n < stream_data->num_transfers; n++) {
+ if (stream_data->transfer_status[i] == TRANSFER_AVAIL) {
+
+ if (stream_data->i != i &&
+ stream_data->out_of_order_event == false) {
+
+ log_warning("Transfer callback occurred out of order. "
+ "(Warning only this time.)\n");
+ stream_data->out_of_order_event = true;
+ }
+
+ stream_data->i = i;
+ return stream_data->transfers[i];
+ }
+
+ i = (i + 1) % stream_data->num_transfers;
+ }
+
+ return NULL;
+}
+
+/* Precondition: A transfer is available. */
+static int submit_transfer(struct bladerf_stream *stream, void *buffer, size_t len)
+{
+ int status;
+ struct bladerf_lusb *lusb = lusb_backend(stream->dev);
+ struct lusb_stream_data *stream_data = stream->backend_data;
+ struct libusb_transfer *transfer;
+ const size_t bytes_per_buffer = async_stream_buf_bytes(stream);
+ size_t prev_idx;
+ const unsigned char ep =
+ (stream->layout & BLADERF_DIRECTION_MASK) == BLADERF_TX ? SAMPLE_EP_OUT : SAMPLE_EP_IN;
+
+ transfer = get_next_available_transfer(stream_data);
+ assert(transfer != NULL);
+
+ assert(bytes_per_buffer <= INT_MAX);
+ libusb_fill_bulk_transfer(transfer,
+ lusb->handle,
+ ep,
+ buffer,
+ (int)len,
+ lusb_stream_cb,
+ stream,
+ stream->transfer_timeout);
+
+ prev_idx = stream_data->i;
+ stream_data->transfer_status[stream_data->i] = TRANSFER_IN_FLIGHT;
+ stream_data->i = (stream_data->i + 1) % stream_data->num_transfers;
+ assert(stream_data->num_avail != 0);
+ stream_data->num_avail--;
+
+ /* FIXME We have an inherent issue here with lock ordering between
+ * stream->lock and libusb's underlying event lock, so we
+ * have to drop the stream->lock as a workaround.
+ *
+ * This implies that a callback can potentially execute,
+ * including a callback for this transfer. Therefore, the transfer
+ * has been setup and its metadata logged.
+ *
+ * Ultimately, we need to review our async scheme and associated
+ * lock schemes.
+ */
+ MUTEX_UNLOCK(&stream->lock);
+ status = libusb_submit_transfer(transfer);
+ MUTEX_LOCK(&stream->lock);
+
+ if (status != 0) {
+ log_error("Failed to submit transfer in %s: %s\n",
+ __FUNCTION__, libusb_error_name(status));
+
+ /* We need to undo the metadata we updated prior to dropping
+ * the lock and attempting to submit the transfer */
+ assert(stream_data->transfer_status[prev_idx] == TRANSFER_IN_FLIGHT);
+ stream_data->transfer_status[prev_idx] = TRANSFER_AVAIL;
+ stream_data->num_avail++;
+ if (stream_data->i == 0) {
+ stream_data->i = stream_data->num_transfers - 1;
+ } else {
+ stream_data->i--;
+ }
+ }
+
+ return error_conv(status);
+}
+
+static int lusb_init_stream(void *driver, struct bladerf_stream *stream,
+ size_t num_transfers)
+{
+ int status = 0;
+ size_t i;
+ struct lusb_stream_data *stream_data;
+
+ /* Fill in backend stream information */
+ stream_data = malloc(sizeof(struct lusb_stream_data));
+ if (!stream_data) {
+ return BLADERF_ERR_MEM;
+ }
+
+ /* Backend stream information */
+ stream->backend_data = stream_data;
+ stream_data->transfers = NULL;
+ stream_data->transfer_status = NULL;
+ stream_data->num_transfers = num_transfers;
+ stream_data->num_avail = 0;
+ stream_data->i = 0;
+ stream_data->out_of_order_event = false;
+
+ stream_data->transfers =
+ malloc(num_transfers * sizeof(struct libusb_transfer *));
+
+ if (stream_data->transfers == NULL) {
+ log_error("Failed to allocate libusb tranfers\n");
+ status = BLADERF_ERR_MEM;
+ goto error;
+ }
+
+ stream_data->transfer_status =
+ calloc(num_transfers, sizeof(transfer_status));
+
+ if (stream_data->transfer_status == NULL) {
+ log_error("Failed to allocated libusb transfer status array\n");
+ status = BLADERF_ERR_MEM;
+ goto error;
+ }
+
+ /* Create the libusb transfers */
+ for (i = 0; i < stream_data->num_transfers; i++) {
+ stream_data->transfers[i] = libusb_alloc_transfer(0);
+
+ /* Upon error, start tearing down anything we've started allocating
+ * and report that the stream is in a bad state */
+ if (stream_data->transfers[i] == NULL) {
+
+ /* Note: <= 0 not appropriate as we're dealing
+ * with an unsigned index */
+ while (i > 0) {
+ if (--i) {
+ libusb_free_transfer(stream_data->transfers[i]);
+ stream_data->transfers[i] = NULL;
+ stream_data->transfer_status[i] = TRANSFER_UNINITIALIZED;
+ stream_data->num_avail--;
+ }
+ }
+
+ status = BLADERF_ERR_MEM;
+ break;
+ } else {
+ stream_data->transfer_status[i] = TRANSFER_AVAIL;
+ stream_data->num_avail++;
+ }
+ }
+
+error:
+ if (status != 0) {
+ free(stream_data->transfer_status);
+ free(stream_data->transfers);
+ free(stream_data);
+ stream->backend_data = NULL;
+ }
+
+ return status;
+}
+
+static int lusb_stream(void *driver, struct bladerf_stream *stream,
+ bladerf_channel_layout layout)
+{
+ size_t i;
+ int status = 0;
+ void *buffer;
+ struct bladerf_metadata metadata;
+ struct bladerf *dev = stream->dev;
+ struct bladerf_lusb *lusb = (struct bladerf_lusb *) driver;
+ struct lusb_stream_data *stream_data = stream->backend_data;
+ struct timeval tv = { 0, LIBUSB_HANDLE_EVENTS_TIMEOUT_NSEC };
+
+ /* Currently unused, so zero it out for a sanity check when debugging */
+ memset(&metadata, 0, sizeof(metadata));
+
+ MUTEX_LOCK(&stream->lock);
+
+ /* Set up initial set of buffers */
+ for (i = 0; i < stream_data->num_transfers; i++) {
+ if ((layout & BLADERF_DIRECTION_MASK) == BLADERF_TX) {
+ buffer = stream->cb(dev,
+ stream,
+ &metadata,
+ NULL,
+ stream->samples_per_buffer,
+ stream->user_data);
+
+ if (buffer == BLADERF_STREAM_SHUTDOWN) {
+ /* If we have transfers in flight and the user prematurely
+ * cancels the stream, we'll start shutting down */
+ if (stream_data->num_avail != stream_data->num_transfers) {
+ stream->state = STREAM_SHUTTING_DOWN;
+ } else {
+ /* No transfers have been shipped out yet so we can
+ * simply enter our "done" state */
+ stream->state = STREAM_DONE;
+ }
+
+ /* In either of the above we don't want to attempt to
+ * get any more buffers from the user */
+ break;
+ }
+ } else {
+ buffer = stream->buffers[i];
+ }
+
+ if (buffer != BLADERF_STREAM_NO_DATA) {
+ if((layout & BLADERF_DIRECTION_MASK) == BLADERF_TX
+ && stream->format == BLADERF_FORMAT_PACKET_META) {
+ status = submit_transfer(stream, buffer, metadata.actual_count);
+ } else {
+ status = submit_transfer(stream, buffer, async_stream_buf_bytes(stream));
+ }
+
+ /* If we failed to submit any transfers, cancel everything in
+ * flight. We'll leave the stream in the running state so we can
+ * have libusb fire off callbacks with the cancelled status*/
+ if (status < 0) {
+ stream->error_code = status;
+ cancel_all_transfers(stream);
+ }
+ }
+ }
+ MUTEX_UNLOCK(&stream->lock);
+
+ /* This loop is required so libusb can do callbacks and whatnot */
+ while (stream->state != STREAM_DONE) {
+ status = libusb_handle_events_timeout(lusb->context, &tv);
+
+ if (status < 0 && status != LIBUSB_ERROR_INTERRUPTED) {
+ log_warning("unexpected value from events processing: "
+ "%d: %s\n", status, libusb_error_name(status));
+ status = error_conv(status);
+ }
+ }
+
+ return status;
+}
+
+/* The top-level code will have aquired the stream->lock for us */
+int lusb_submit_stream_buffer(void *driver, struct bladerf_stream *stream,
+ void *buffer, size_t *length,
+ unsigned int timeout_ms, bool nonblock)
+{
+ int status = 0;
+ struct lusb_stream_data *stream_data = stream->backend_data;
+ struct timespec timeout_abs;
+
+ if (buffer == BLADERF_STREAM_SHUTDOWN) {
+ if (stream_data->num_avail == stream_data->num_transfers) {
+ stream->state = STREAM_DONE;
+ } else {
+ stream->state = STREAM_SHUTTING_DOWN;
+ }
+
+ return 0;
+ }
+
+ if (stream_data->num_avail == 0) {
+ if (nonblock) {
+ log_debug("Non-blocking buffer submission requested, but no "
+ "transfers are currently available.\n");
+
+ return BLADERF_ERR_WOULD_BLOCK;
+ }
+
+ if (timeout_ms != 0) {
+ status = populate_abs_timeout(&timeout_abs, timeout_ms);
+ if (status != 0) {
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ while (stream_data->num_avail == 0 && status == 0) {
+ status = pthread_cond_timedwait(&stream->can_submit_buffer,
+ &stream->lock,
+ &timeout_abs);
+ }
+ } else {
+ while (stream_data->num_avail == 0 && status == 0) {
+ status = pthread_cond_wait(&stream->can_submit_buffer,
+ &stream->lock);
+ }
+ }
+ }
+
+ if (status == ETIMEDOUT) {
+ log_debug("%s: Timed out waiting for a transfer to become available.\n",
+ __FUNCTION__);
+ return BLADERF_ERR_TIMEOUT;
+ } else if (status != 0) {
+ return BLADERF_ERR_UNEXPECTED;
+ } else {
+ return submit_transfer(stream, buffer, *length);
+ }
+}
+
+static int lusb_deinit_stream(void *driver, struct bladerf_stream *stream)
+{
+ size_t i;
+ struct lusb_stream_data *stream_data = stream->backend_data;
+
+ for (i = 0; i < stream_data->num_transfers; i++) {
+ libusb_free_transfer(stream_data->transfers[i]);
+ stream_data->transfers[i] = NULL;
+ stream_data->transfer_status[i] = TRANSFER_UNINITIALIZED;
+ }
+
+ free(stream_data->transfers);
+ free(stream_data->transfer_status);
+ free(stream->backend_data);
+
+ stream->backend_data = NULL;
+ return 0;
+}
+
+static const struct usb_fns libusb_fns = {
+ FIELD_INIT(.probe, lusb_probe),
+ FIELD_INIT(.open, lusb_open),
+ FIELD_INIT(.close, lusb_close),
+ FIELD_INIT(.get_vid_pid, lusb_get_vid_pid),
+ FIELD_INIT(.get_flash_id, NULL),
+ FIELD_INIT(.get_handle, lusb_get_handle),
+ FIELD_INIT(.get_speed, lusb_get_speed),
+ FIELD_INIT(.change_setting, lusb_change_setting),
+ FIELD_INIT(.control_transfer, lusb_control_transfer),
+ FIELD_INIT(.bulk_transfer, lusb_bulk_transfer),
+ FIELD_INIT(.get_string_descriptor, lusb_get_string_descriptor),
+ FIELD_INIT(.init_stream, lusb_init_stream),
+ FIELD_INIT(.stream, lusb_stream),
+ FIELD_INIT(.submit_stream_buffer, lusb_submit_stream_buffer),
+ FIELD_INIT(.deinit_stream, lusb_deinit_stream),
+ FIELD_INIT(.open_bootloader, lusb_open_bootloader),
+ FIELD_INIT(.close_bootloader, lusb_close_bootloader),
+};
+
+const struct usb_driver usb_driver_libusb = {
+ FIELD_INIT(.fn, &libusb_fns),
+ FIELD_INIT(.id, BLADERF_BACKEND_LIBUSB),
+};