diff options
Diffstat (limited to 'Radio/HW/BladeRF/src/backend/usb/cyapi.c')
-rw-r--r-- | Radio/HW/BladeRF/src/backend/usb/cyapi.c | 854 |
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), + }; +} |