summaryrefslogtreecommitdiff
path: root/Radio/HW/BladeRF/src/backend/usb/cyapi.c
diff options
context:
space:
mode:
Diffstat (limited to 'Radio/HW/BladeRF/src/backend/usb/cyapi.c')
-rw-r--r--Radio/HW/BladeRF/src/backend/usb/cyapi.c854
1 files changed, 854 insertions, 0 deletions
diff --git a/Radio/HW/BladeRF/src/backend/usb/cyapi.c b/Radio/HW/BladeRF/src/backend/usb/cyapi.c
new file mode 100644
index 0000000..48bef43
--- /dev/null
+++ b/Radio/HW/BladeRF/src/backend/usb/cyapi.c
@@ -0,0 +1,854 @@
+/*
+ * 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
+ */
+
+/* This is a Windows-specific USB backend using Cypress's CyAPI, which utilizes
+ * the CyUSB3.sys driver (with a CyUSB3.inf modified to include the bladeRF
+ * VID/PID).
+ */
+
+extern "C" {
+#include <stdlib.h>
+#include <pthread.h>
+#include <errno.h>
+#include "bladeRF.h" /* Firmware interface */
+
+#include "devinfo.h"
+#include "backend/backend.h"
+#include "backend/usb/usb.h"
+#include "streaming/async.h"
+#include "helpers/timeout.h"
+#include "log.h"
+}
+
+#include <CyAPI.h>
+
+/* This GUID must match that in the modified CyUSB3.inf used with the bladeRF */
+static const GUID driver_guid = {
+ 0x35D5D3F1, 0x9D0E, 0x4F62, 0xBC, 0xFB, 0xB0, 0xD4, 0x8E,0xA6, 0x34, 0x16
+};
+
+/* "Private data" for the CyAPI backend */
+struct bladerf_cyapi {
+ CCyUSBDevice *dev;
+ HANDLE mutex;
+};
+
+struct transfer {
+ OVERLAPPED event; /* Transfer completion event handle */
+ PUCHAR handle; /* Handle for in-flight transfer */
+ PUCHAR buffer; /* Buffer associated with transfer */
+};
+
+struct stream_data {
+ CCyBulkEndPoint *ep; /* Endpoint associated with the stream */
+
+ struct transfer *transfers; /* Transfer slots */
+
+ size_t num_transfers; /* Max # of in-flight transfers */
+ size_t num_avail;
+ size_t avail_i; /* Index of next available transfer slot */
+ size_t inflight_i; /* Index of in-flight transfer that is
+ * expected to finish next */
+};
+
+static inline struct bladerf_cyapi * get_backend_data(void *driver)
+{
+ assert(driver);
+ return (struct bladerf_cyapi *) driver;
+}
+
+static inline CCyUSBDevice * get_device(void *driver)
+{
+ struct bladerf_cyapi *backend_data = get_backend_data(driver);
+ assert(backend_data->dev);
+ return backend_data->dev;
+}
+
+static inline struct stream_data * get_stream_data(struct bladerf_stream *s)
+{
+ assert(s && s->backend_data);
+ return (struct stream_data *) s->backend_data;
+}
+
+static int open_device(CCyUSBDevice *dev, int instance, HANDLE *mutex)
+{
+ int status = 0;
+ bool success;
+ DWORD last_error;
+ 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);
+
+ if (mutex_name == NULL) {
+ return BLADERF_ERR_MEM;
+ } else {
+ strcpy(mutex_name, prefix);
+ }
+
+ success = dev->Open(instance);
+ if (!success) {
+ status = BLADERF_ERR_IO;
+ goto out;
+ }
+
+ wcstombs(mutex_name + strlen(prefix), dev->SerialNumber, sizeof(mutex_name) - BLADERF_SERIAL_LENGTH - 1);
+ log_verbose("Mutex name: %s\n", mutex_name);
+
+ SetLastError(0);
+ *mutex = CreateMutex(NULL, true, mutex_name);
+ last_error = GetLastError();
+ if (mutex == NULL || last_error != 0) {
+ log_debug("Unable to create device mutex: mutex=%p, last_error=%ld\n",
+ mutex, last_error);
+ dev->Close();
+ status = BLADERF_ERR_NODEV;
+ }
+
+out:
+ free(mutex_name);
+ return status;
+}
+
+static inline bool has_bladerf_ids(CCyUSBDevice *dev)
+{
+ return ((dev->VendorID == USB_NUAND_VENDOR_ID) && (dev->ProductID == USB_NUAND_BLADERF_PRODUCT_ID)) ||
+ ((dev->VendorID == USB_NUAND_VENDOR_ID) && (dev->ProductID == USB_NUAND_BLADERF2_PRODUCT_ID)) ||
+ ((dev->VendorID == USB_NUAND_LEGACY_VENDOR_ID) && (dev->ProductID == USB_NUAND_BLADERF_LEGACY_PRODUCT_ID));
+}
+
+static inline bool has_boot_ids(CCyUSBDevice *dev)
+{
+ return ((dev->VendorID == USB_CYPRESS_VENDOR_ID) && (dev->ProductID == USB_FX3_PRODUCT_ID)) ||
+ ((dev->VendorID == USB_NUAND_VENDOR_ID) && (dev->ProductID == USB_NUAND_BLADERF_BOOT_PRODUCT_ID)) ||
+ ((dev->VendorID == USB_NUAND_LEGACY_VENDOR_ID) && (dev->ProductID == USB_NUAND_BLADERF_LEGACY_BOOT_PRODUCT_ID));
+}
+
+static bool device_matches_target(CCyUSBDevice *dev,
+ backend_probe_target target)
+{
+ bool matches = false;
+
+ switch (target) {
+ case BACKEND_PROBE_BLADERF:
+ matches = has_bladerf_ids(dev);
+ break;
+
+ case BACKEND_PROBE_FX3_BOOTLOADER:
+ matches = has_boot_ids(dev);
+ break;
+ }
+
+ return matches;
+}
+
+static int cyapi_probe(backend_probe_target probe_target,
+ struct bladerf_devinfo_list *info_list)
+{
+ CCyUSBDevice *dev = new CCyUSBDevice(NULL, driver_guid);
+ if (dev == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ for (int i = 0; i < dev->DeviceCount(); i++) {
+ struct bladerf_devinfo info;
+ bool opened;
+ int status;
+
+ opened = dev->Open(i);
+ if (opened) {
+ if (device_matches_target(dev, probe_target)) {
+ const size_t max_serial = sizeof(info.serial) - 1;
+ info.instance = i;
+ memset(info.serial, 0, sizeof(info.serial));
+ wcstombs(info.serial, dev->SerialNumber, max_serial);
+ info.usb_addr = dev->USBAddress;
+ info.usb_bus = 0; /* CyAPI doesn't provide this */
+ info.backend = BLADERF_BACKEND_CYPRESS;
+ status = bladerf_devinfo_list_add(info_list, &info);
+ if (status != 0) {
+ 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);
+ }
+ }
+
+ dev->Close();
+ }
+ }
+
+ delete dev;
+ return 0;
+}
+
+static int open_via_info(void **driver, backend_probe_target probe_target,
+ struct bladerf_devinfo *info_in,
+ struct bladerf_devinfo *info_out)
+{
+ int status;
+ int instance = -1;
+ struct bladerf_devinfo_list info_list;
+ CCyUSBDevice *dev;
+ struct bladerf_cyapi *cyapi_data;
+
+ dev = new CCyUSBDevice(NULL, driver_guid);
+ if (dev == NULL) {
+ return BLADERF_ERR_MEM;
+ }
+
+ cyapi_data = (struct bladerf_cyapi *) calloc(1, sizeof(cyapi_data[0]));
+ if (cyapi_data == NULL) {
+ delete dev;
+ return BLADERF_ERR_MEM;
+ }
+
+ bladerf_devinfo_list_init(&info_list);
+ status = cyapi_probe(probe_target, &info_list);
+
+ for (unsigned int i = 0; i < info_list.num_elt && status == 0; i++) {
+
+
+ if (bladerf_devinfo_matches(&info_list.elt[i], info_in)) {
+ instance = info_list.elt[i].instance;
+
+ status = open_device(dev, instance, &cyapi_data->mutex);
+ if (status == 0) {
+ cyapi_data->dev = dev;
+ *driver = cyapi_data;
+ if (info_out != NULL) {
+ memcpy(info_out,
+ &info_list.elt[instance],
+ sizeof(info_out[0]));
+ }
+ status = 0;
+ break;
+ }
+ }
+ }
+
+ if (status == 0 && instance < 0) {
+ status = BLADERF_ERR_NODEV;
+ }
+
+ if (status != 0) {
+ delete dev;
+ ReleaseMutex(cyapi_data->mutex);
+ CloseHandle(cyapi_data->mutex);
+ }
+
+ free(info_list.elt);
+ return status;
+}
+
+
+static int cyapi_open(void **driver,
+ struct bladerf_devinfo *info_in,
+ struct bladerf_devinfo *info_out)
+{
+ return open_via_info(driver, BACKEND_PROBE_BLADERF, info_in, info_out);
+}
+
+static int cyapi_change_setting(void *driver, uint8_t setting)
+{
+ CCyUSBDevice *dev = get_device(driver);
+
+ if (dev->SetAltIntfc(setting)) {
+ return 0;
+ } else {
+ return BLADERF_ERR_IO;
+ }
+}
+
+static void cyapi_close(void *driver)
+{
+ struct bladerf_cyapi *cyapi_data = get_backend_data(driver);
+ cyapi_data->dev->Close();
+ delete cyapi_data->dev;
+ ReleaseMutex(cyapi_data->mutex);
+ CloseHandle(cyapi_data->mutex);
+ free(driver);
+
+}
+
+static int cyapi_get_vid_pid(void *driver, uint16_t *vid,
+ uint16_t *pid)
+{
+ CCyUSBDevice *dev = get_device(driver);
+
+ *vid = dev->VendorID;
+ *pid = dev->ProductID;
+
+ return 0;
+}
+
+static int cyapi_get_flash_id(void *driver, uint8_t *mid, uint8_t *did)
+{
+ return BLADERF_ERR_UNSUPPORTED;
+}
+
+static int cyapi_get_handle(void *driver, void **handle)
+{
+ CCyUSBDevice *dev = get_device(driver);
+ *handle = dev;
+
+ return 0;
+}
+static int cyapi_get_speed(void *driver,
+ bladerf_dev_speed *device_speed)
+{
+ int status = 0;
+ CCyUSBDevice *dev = get_device(driver);
+
+ if (dev->bHighSpeed) {
+ *device_speed = BLADERF_DEVICE_SPEED_HIGH;
+ } else if (dev->bSuperSpeed) {
+ *device_speed = BLADERF_DEVICE_SPEED_SUPER;
+ } else {
+ *device_speed = BLADERF_DEVICE_SPEED_UNKNOWN;
+ status = BLADERF_ERR_UNEXPECTED;
+ log_debug("%s: Unable to determine device speed.\n", __FUNCTION__);
+ }
+
+ return status;
+}
+
+static int cyapi_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)
+{
+ CCyUSBDevice *dev = get_device(driver);
+ LONG len;
+ bool success;
+
+ int status = 0;
+
+ switch (dir) {
+ case USB_DIR_DEVICE_TO_HOST:
+ dev->ControlEndPt->Direction = DIR_FROM_DEVICE;
+ break;
+ case USB_DIR_HOST_TO_DEVICE:
+ dev->ControlEndPt->Direction = DIR_TO_DEVICE;
+ break;
+ }
+
+ switch (req_type) {
+ case USB_REQUEST_CLASS:
+ dev->ControlEndPt->ReqType = REQ_CLASS;
+ break;
+ case USB_REQUEST_STANDARD:
+ dev->ControlEndPt->ReqType = REQ_STD;
+ break;
+ case USB_REQUEST_VENDOR:
+ dev->ControlEndPt->ReqType = REQ_VENDOR;
+ break;
+ }
+
+ switch (target_type) {
+ case USB_TARGET_DEVICE:
+ dev->ControlEndPt->Target = TGT_DEVICE;
+ break;
+ case USB_TARGET_ENDPOINT:
+ dev->ControlEndPt->Target = TGT_ENDPT;
+ break;
+ case USB_TARGET_INTERFACE:
+ dev->ControlEndPt->Target = TGT_INTFC;
+ break;
+ case USB_TARGET_OTHER:
+ dev->ControlEndPt->Target = TGT_OTHER;
+ break;
+ }
+
+ dev->ControlEndPt->MaxPktSize = buffer_len;
+ dev->ControlEndPt->ReqCode = request;
+ dev->ControlEndPt->Index = windex;
+ dev->ControlEndPt->Value = wvalue;
+ dev->ControlEndPt->TimeOut = timeout_ms ? timeout_ms : INFINITE;
+ len = buffer_len;
+
+ success = dev->ControlEndPt->XferData((PUCHAR)buffer, len);
+ if (!success) {
+ status = BLADERF_ERR_IO;
+ }
+
+ return status;
+
+}
+
+static CCyBulkEndPoint *get_ep(CCyUSBDevice *USBDevice, int id)
+{
+
+ int count;
+ count = USBDevice->EndPointCount();
+
+ for (int i = 1; i < count; i++) {
+ if (USBDevice->EndPoints[i]->Address == id) {
+ return (CCyBulkEndPoint *) USBDevice->EndPoints[i];
+ }
+ }
+
+ return NULL;
+}
+
+static int cyapi_bulk_transfer(void *driver, uint8_t endpoint, void *buffer,
+ uint32_t buffer_len, uint32_t timeout_ms)
+{
+ bool success;
+ int status = BLADERF_ERR_IO;
+ CCyUSBDevice *dev = get_device(driver);
+ CCyBulkEndPoint *ep = get_ep(dev, endpoint);
+
+ if (ep != NULL) {
+ LONG len = buffer_len;
+ dev->ControlEndPt->TimeOut = timeout_ms ? timeout_ms : INFINITE;
+ success = ep->XferData((PUCHAR)buffer, len);
+
+ if (success) {
+ if (len == buffer_len) {
+ status = 0;
+ } else {
+ log_debug("Transfer len mismatch: %u vs %u\n",
+ (unsigned int) buffer_len, (unsigned int) len);
+ }
+ } else {
+ log_debug("Transfered failed.\n");
+ }
+ } else {
+ log_debug("Failed to get EP handle.\n");
+ }
+
+ return status;
+}
+
+static int cyapi_get_string_descriptor(void *driver, uint8_t index,
+ void *buffer, uint32_t buffer_len)
+{
+ int status;
+ char *str;
+
+ memset(buffer, 0, buffer_len);
+
+ status = cyapi_control_transfer(driver,
+ USB_TARGET_DEVICE,
+ USB_REQUEST_STANDARD,
+ USB_DIR_DEVICE_TO_HOST,
+ 0x06, 0x0300 | index, 0,
+ buffer, buffer_len,
+ BLADE_USB_TIMEOUT_MS);
+
+ if (status != 0) {
+ return status;
+ }
+
+ str = (char*)buffer;
+ for (unsigned int i = 0; i < (buffer_len / 2); i++) {
+ str[i] = str[2 + (i * 2)];
+ }
+
+ return 0;
+}
+
+static int cyapi_deinit_stream(void *driver, struct bladerf_stream *stream)
+{
+ struct stream_data *data;
+
+ assert(stream != NULL);
+ data = get_stream_data(stream);
+ assert(data != NULL);
+
+ for (unsigned int i = 0; i < data->num_transfers; i++) {
+ CloseHandle(data->transfers[i].event.hEvent);
+ }
+
+ free(data->transfers);
+ free(data);
+
+ return 0;
+}
+
+static int cyapi_init_stream(void *driver, struct bladerf_stream *stream,
+ size_t num_transfers)
+{
+ int status = BLADERF_ERR_MEM;
+ CCyUSBDevice *dev = get_device(driver);
+ struct stream_data *data;
+
+ data = (struct stream_data *) calloc(1, sizeof(data[0]));
+ if (data != NULL) {
+ stream->backend_data = data;
+ } else {
+ return status;
+ }
+
+ data->transfers =
+ (struct transfer*) calloc(num_transfers, sizeof(struct transfer));
+
+ if (data->transfers == NULL) {
+ goto out;
+ }
+
+ for (unsigned int i = 0; i < num_transfers; i++) {
+ data->transfers[i].event.hEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+ if (data->transfers[i].event.hEvent == NULL) {
+ log_debug("%s: Failed to create EventObject for transfer %u\n",
+ (unsigned int) i);
+ goto out;
+ }
+ }
+
+ data->num_transfers = num_transfers;
+ data->num_avail = num_transfers;
+ data->inflight_i = 0;
+ data->avail_i = 0;
+
+ status = 0;
+
+out:
+ if (status != 0) {
+ cyapi_deinit_stream(driver, stream);
+ stream->backend_data = NULL;
+ }
+
+ return status;
+}
+
+#ifdef LOGGING_ENABLED
+static inline int find_buf(void* ptr, struct bladerf_stream *stream)
+{
+ for (unsigned int i=0; i<stream->num_buffers; i++) {
+ if (stream->buffers[i] == ptr) {
+ return i;
+ }
+ }
+
+ log_debug("Unabled to find buffer %p\n:", ptr);
+ return -1;
+}
+#endif
+
+#ifndef ENABLE_LIBBLADERF_ASYNC_LOG_VERBOSE
+#undef log_verbose
+#define log_verbose(...)
+#endif
+
+static inline size_t next_idx(struct stream_data *data, size_t i)
+{
+ return (i + 1) % data->num_transfers;
+}
+
+/* Assumes a transfer is available and the stream lock is being held */
+static int submit_transfer(struct bladerf_stream *stream, void *buffer, size_t len)
+{
+ int status = 0;
+ PUCHAR xfer;
+ struct stream_data *data = get_stream_data(stream);
+
+ LONG buffer_size = (LONG) len;
+
+ assert(data->transfers[data->avail_i].handle == NULL);
+ assert(data->transfers[data->avail_i].buffer == NULL);
+ assert(data->num_avail != 0);
+
+ xfer = data->ep->BeginDataXfer((PUCHAR) buffer, buffer_size,
+ &data->transfers[data->avail_i].event);
+
+ if (xfer != NULL) {
+ data->transfers[data->avail_i].handle = xfer;
+ data->transfers[data->avail_i].buffer = (PUCHAR) buffer;
+
+ log_verbose("Submitted buffer %p using transfer slot %u.\n",
+ buffer, (unsigned int) data->avail_i);
+
+ data->avail_i = next_idx(data, data->avail_i);
+ data->num_avail--;
+ } else {
+ status = BLADERF_ERR_UNEXPECTED;
+ log_debug("Failed to submit buffer %p in transfer slot %u.\n",
+ buffer, (unsigned int) data->avail_i);
+ }
+
+ return status;
+}
+
+static int cyapi_stream(void *driver, struct bladerf_stream *stream,
+ bladerf_channel_layout layout)
+{
+ int status;
+ int idx = 0;
+ long len;
+ void *next_buffer;
+ ULONG timeout_ms;
+ bool success, done;
+ struct stream_data *data = get_stream_data(stream);
+ struct bladerf_cyapi *cyapi = get_backend_data(driver);
+ struct bladerf_metadata meta;
+
+ assert(stream->transfer_timeout <= ULONG_MAX);
+ if (stream->transfer_timeout == 0) {
+ timeout_ms = INFINITE;
+ } else {
+ timeout_ms = stream->transfer_timeout;
+ }
+
+ switch (layout & BLADERF_DIRECTION_MASK) {
+ case BLADERF_RX:
+ data->ep = get_ep(cyapi->dev, SAMPLE_EP_IN);
+ break;
+
+ case BLADERF_TX:
+ data->ep = get_ep(cyapi->dev, SAMPLE_EP_OUT);
+ break;
+ }
+
+ if (data->ep == NULL) {
+ log_debug("Failed to get EP handle.\n");
+ return BLADERF_ERR_UNEXPECTED;
+ }
+
+ data->ep->XferMode = XMODE_DIRECT;
+ data->ep->Abort();
+ data->ep->Reset();
+
+ log_verbose("Starting stream...\n");
+ status = 0;
+ done = false;
+ memset(&meta, 0, sizeof(meta));
+
+ MUTEX_LOCK(&stream->lock);
+
+ for (unsigned int i = 0; i < data->num_transfers && status == 0; i++) {
+ if ((layout & BLADERF_DIRECTION_MASK) == BLADERF_TX) {
+ next_buffer = stream->cb(stream->dev, stream, &meta, NULL,
+ stream->samples_per_buffer,
+ stream->user_data);
+
+ if (next_buffer == BLADERF_STREAM_SHUTDOWN) {
+ done = true;
+ break;
+ } else if (next_buffer == BLADERF_STREAM_NO_DATA) {
+ continue;
+ }
+ } else {
+ next_buffer = stream->buffers[i];
+ }
+
+ if ((stream->layout & BLADERF_DIRECTION_MASK) == BLADERF_TX
+ && stream->format == BLADERF_FORMAT_PACKET_META) {
+ status = submit_transfer(stream, next_buffer, meta.actual_count);
+ } else {
+ status = submit_transfer(stream, next_buffer, async_stream_buf_bytes(stream));
+ }
+ }
+
+ MUTEX_UNLOCK(&stream->lock);
+ if (status != 0) {
+ goto out;
+ }
+
+ while (!done) {
+ struct transfer *xfer;
+ size_t i;
+
+ i = data->inflight_i;
+ xfer = &data->transfers[i];
+ success = data->ep->WaitForXfer(&xfer->event, timeout_ms);
+
+ if (!success) {
+ status = BLADERF_ERR_TIMEOUT;
+ log_debug("Steam timed out.\n");
+ break;
+ }
+
+ len = 0;
+ next_buffer = NULL;
+
+ log_verbose("Got transfer complete in slot %u (buffer %p)\n",
+ i, data->transfers[i].buffer);
+
+ MUTEX_LOCK(&stream->lock);
+ success = data->ep->FinishDataXfer(data->transfers[i].buffer, (LONG &)len,
+ &data->transfers[i].event,
+ xfer->handle);
+
+ if (success) {
+ next_buffer = stream->cb(stream->dev, stream, &meta,
+ data->transfers[i].buffer,
+ bytes_to_samples(stream->format, (LONG &)len),
+ stream->user_data);
+
+ } else {
+ done = true;
+ status = BLADERF_ERR_IO;
+ log_debug("Failed to finish transfer %u, buf=%p.\n",
+ (unsigned int)i, &data->transfers[i].buffer);
+ }
+
+ data->transfers[i].buffer = NULL;
+ data->transfers[i].handle = NULL;
+ data->num_avail++;
+ pthread_cond_signal(&stream->can_submit_buffer);
+
+ if (next_buffer == BLADERF_STREAM_SHUTDOWN) {
+ done = true;
+ } else if (next_buffer != BLADERF_STREAM_NO_DATA) {
+ if ((layout & BLADERF_DIRECTION_MASK) == BLADERF_TX
+ && stream->format == BLADERF_FORMAT_PACKET_META) {
+ status = submit_transfer(stream, next_buffer, meta.actual_count);
+ } else {
+ status = submit_transfer(stream, next_buffer, async_stream_buf_bytes(stream));
+ }
+
+ done = (status != 0);
+ }
+
+ data->inflight_i = next_idx(data, data->inflight_i);
+ MUTEX_UNLOCK(&stream->lock);
+ }
+
+out:
+
+ MUTEX_LOCK(&stream->lock);
+ stream->error_code = status;
+ stream->state = STREAM_SHUTTING_DOWN;
+
+ data->ep->Abort();
+ data->ep->Reset();
+
+
+ for (unsigned int i = 0; i < data->num_transfers; i++) {
+ LONG len = 0;
+ if (data->transfers[i].handle != NULL) {
+ data->ep->FinishDataXfer(data->transfers[i].buffer, (LONG &)len,
+ &data->transfers[i].event,
+ data->transfers[i].handle);
+
+
+ data->transfers[i].buffer = NULL;
+ data->transfers[i].handle = NULL;
+ data->num_avail++;
+ }
+ }
+
+ assert(data->num_avail == data->num_transfers);
+
+ stream->state = STREAM_DONE;
+ log_verbose("Stream done (error_code = %d)\n", stream->error_code);
+ MUTEX_UNLOCK(&stream->lock);
+
+ return 0;
+}
+
+/* The top-level code will have acquired the stream->lock for us */
+int cyapi_submit_stream_buffer(void *driver, struct bladerf_stream *stream,
+ void *buffer, size_t *length,
+ unsigned int timeout_ms, bool nonblock)
+{
+ int status = 0;
+ struct timespec timeout_abs;
+ struct stream_data *data = get_stream_data(stream);
+
+ if (buffer == BLADERF_STREAM_SHUTDOWN) {
+ if (data->num_avail == data->num_transfers) {
+ stream->state = STREAM_DONE;
+ } else {
+ stream->state = STREAM_SHUTTING_DOWN;
+ }
+
+ return 0;
+ }
+
+ if (data->num_avail == 0) {
+ if (nonblock) {
+ log_debug("Non-blocking buffer submission requested, but no "
+ "transfers are currently available.");
+
+ 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 (data->num_avail == 0 && status == 0) {
+ status = pthread_cond_timedwait(&stream->can_submit_buffer,
+ &stream->lock,
+ &timeout_abs);
+ }
+ } else {
+ while (data->num_avail == 0 && status == 0) {
+ status = pthread_cond_wait(&stream->can_submit_buffer,
+ &stream->lock);
+ }
+ }
+ }
+
+ if (status == ETIMEDOUT) {
+ return BLADERF_ERR_TIMEOUT;
+ } else if (status != 0) {
+ return BLADERF_ERR_UNEXPECTED;
+ } else {
+ return submit_transfer(stream, buffer, *length);
+ }
+}
+
+int cyapi_open_bootloader(void **driver, uint8_t bus, uint8_t addr)
+{
+ struct bladerf_devinfo info;
+
+ bladerf_init_devinfo(&info);
+ info.backend = BLADERF_BACKEND_CYPRESS;
+ info.usb_bus = bus;
+ info.usb_addr = addr;
+
+ return open_via_info(driver, BACKEND_PROBE_FX3_BOOTLOADER, &info, NULL);
+}
+
+extern "C" {
+ static const struct usb_fns cypress_fns = {
+ FIELD_INIT(.probe, cyapi_probe),
+ FIELD_INIT(.open, cyapi_open),
+ FIELD_INIT(.close, cyapi_close),
+ FIELD_INIT(.get_vid_pid, cyapi_get_vid_pid),
+ FIELD_INIT(.get_flash_id, cyapi_get_flash_id),
+ FIELD_INIT(.get_handle, cyapi_get_handle),
+ FIELD_INIT(.get_speed, cyapi_get_speed),
+ FIELD_INIT(.change_setting, cyapi_change_setting),
+ FIELD_INIT(.control_transfer, cyapi_control_transfer),
+ FIELD_INIT(.bulk_transfer, cyapi_bulk_transfer),
+ FIELD_INIT(.get_string_descriptor, cyapi_get_string_descriptor),
+ FIELD_INIT(.init_stream, cyapi_init_stream),
+ FIELD_INIT(.stream, cyapi_stream),
+ FIELD_INIT(.submit_stream_buffer, cyapi_submit_stream_buffer),
+ FIELD_INIT(.deinit_stream, cyapi_deinit_stream),
+ FIELD_INIT(.open_bootloader, cyapi_open_bootloader),
+ FIELD_INIT(.close_bootloader, cyapi_close),
+ };
+
+ struct usb_driver usb_driver_cypress = {
+ FIELD_INIT(.fn, &cypress_fns),
+ FIELD_INIT(.id, BLADERF_BACKEND_CYPRESS),
+ };
+}