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 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 error; } 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 error; } error: if (status != 0) { if (dev->handle != NULL) { libusb_close(dev->handle); } free(dev); } else { *dev_out = dev; } return status; }
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_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 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; }
/* Precondition: A transfer is available. */ static int submit_transfer(struct bladerf_stream *stream, void *buffer) { int status; struct bladerf_lusb *lusb = lusb_backend(stream->dev); struct lusb_stream_data *stream_data = stream->backend_data; struct libusb_transfer *transfer; size_t bytes_per_buffer; const unsigned char ep = stream->module == BLADERF_MODULE_TX ? SAMPLE_EP_OUT : SAMPLE_EP_IN; assert(stream_data->transfer_status[stream_data->i] == TRANSFER_AVAIL); transfer = stream_data->transfers[stream_data->i]; switch (stream->format) { case BLADERF_FORMAT_SC16_Q11: bytes_per_buffer = c16_samples_to_bytes(stream->samples_per_buffer); break; default: assert(!"Unexpected format"); return BLADERF_ERR_INVAL; } assert(bytes_per_buffer <= INT_MAX); libusb_fill_bulk_transfer(transfer, lusb->handle, ep, buffer, (int)bytes_per_buffer, lusb_stream_cb, stream, stream->dev->transfer_timeout[stream->module]); status = libusb_submit_transfer(transfer); if (status == 0) { 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_transfers != 0); stream_data->num_avail_transfers--; } else { log_error("Failed to submit transfer in %s: %s\n", __FUNCTION__, libusb_error_name(status)); } return error_conv(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 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: requeted=%u, transferred=%u\n", buffer_len, n_transferred); status = BLADERF_ERR_IO; } return status; }
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 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; }
static int lusb_stream(void *driver, struct bladerf_stream *stream, bladerf_module module) { 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 (module == BLADERF_MODULE_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) { status = submit_transfer(stream, buffer); /* 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; }
/* Precondition: A transfer is available. */ static int submit_transfer(struct bladerf_stream *stream, void *buffer) { 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->module == BLADERF_MODULE_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)bytes_per_buffer, lusb_stream_cb, stream, stream->dev->transfer_timeout[stream->module]); 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_open(void **driver, struct bladerf_devinfo *info_in, struct bladerf_devinfo *info_out) { int status, i, n; int fx3_status; ssize_t count; struct bladerf_lusb *lusb = NULL; libusb_device **list = NULL; struct bladerf_devinfo thisinfo; 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)); status = error_conv(status); goto error; } /* 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 /* Iterate through all the USB devices */ count = libusb_get_device_list(context, &list); for (i = 0, n = 0; i < count; i++) { if (device_is_bladerf(list[i])) { log_verbose("Found a bladeRF (based upon VID/PID)\n"); /* Open the USB device and get some information */ status = get_devinfo(list[i], &thisinfo); if(status < 0) { log_debug("Could not open bladeRF device: %s\n", libusb_error_name(status) ); status = error_conv(status); goto error; } thisinfo.instance = n++; /* Check to see if this matches the info struct */ if (bladerf_devinfo_matches(&thisinfo, info_in)) { lusb = (struct bladerf_lusb *)malloc(sizeof(struct bladerf_lusb)); if (lusb == NULL) { log_debug("Skipping instance %d due to failed allocation\n", thisinfo.instance); lusb = NULL; continue; } lusb->context = context; lusb->dev = list[i]; status = libusb_open(list[i], &lusb->handle); if (status < 0) { status = error_conv(status); goto error; } status = libusb_claim_interface(lusb->handle, 0); if(status < 0) { log_debug("Could not claim interface: %s\n", libusb_error_name(status)); status = error_conv(status); goto error; } memcpy(info_out, &thisinfo, sizeof(struct bladerf_devinfo)); *driver = lusb; break; } else { log_verbose("Devinfo doesn't match - skipping" "(instance=%d, serial=%d, bus/addr=%d\n", bladerf_instance_matches(&thisinfo, info_in), bladerf_serial_matches(&thisinfo, info_in), bladerf_bus_addr_matches(&thisinfo, info_in)); } } if (device_is_fx3_bootloader(list[i])) { fx3_status = get_devinfo(list[i], &thisinfo); if (fx3_status != 0) { log_debug("Could not open FX3 bootloader device: %s\n", libusb_error_name(fx3_status)); continue; } log_info("Found FX3 bootloader device on bus=%d addr=%d. " "This may be a bladeRF.\n", thisinfo.usb_bus, thisinfo.usb_addr); log_info("Use bladeRF-cli command \"recover %d %d " "<FX3 firmware>\" to boot the bladeRF firmware.\n", thisinfo.usb_bus, thisinfo.usb_addr); } } error: if (list) { libusb_free_device_list(list, 1); } if (lusb == NULL) { log_debug("No devices available on the libusb backend.\n"); status = BLADERF_ERR_NODEV; } if (status != 0) { if (lusb != NULL) { if (lusb->handle) { libusb_close(lusb->handle); } free(lusb); } libusb_exit(context); } return status; }