From cf4444e7390365df43ecbd3d130015c1e06ef88f Mon Sep 17 00:00:00 2001 From: Arturs Artamonovs Date: Sun, 3 Nov 2024 15:56:55 +0000 Subject: BladeRF library compiles --- Radio/HW/BladeRF/src/backend/usb/cyapi.c | 854 +++++++++++ Radio/HW/BladeRF/src/backend/usb/libusb.c | 1504 ++++++++++++++++++++ Radio/HW/BladeRF/src/backend/usb/nios_access.c | 1322 +++++++++++++++++ Radio/HW/BladeRF/src/backend/usb/nios_access.h | 582 ++++++++ .../BladeRF/src/backend/usb/nios_legacy_access.c | 744 ++++++++++ .../BladeRF/src/backend/usb/nios_legacy_access.h | 488 +++++++ Radio/HW/BladeRF/src/backend/usb/usb.c | 1430 +++++++++++++++++++ Radio/HW/BladeRF/src/backend/usb/usb.h | 168 +++ 8 files changed, 7092 insertions(+) create mode 100644 Radio/HW/BladeRF/src/backend/usb/cyapi.c create mode 100644 Radio/HW/BladeRF/src/backend/usb/libusb.c create mode 100644 Radio/HW/BladeRF/src/backend/usb/nios_access.c create mode 100644 Radio/HW/BladeRF/src/backend/usb/nios_access.h create mode 100644 Radio/HW/BladeRF/src/backend/usb/nios_legacy_access.c create mode 100644 Radio/HW/BladeRF/src/backend/usb/nios_legacy_access.h create mode 100644 Radio/HW/BladeRF/src/backend/usb/usb.c create mode 100644 Radio/HW/BladeRF/src/backend/usb/usb.h (limited to 'Radio/HW/BladeRF/src/backend/usb') 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 +#include +#include +#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 + +/* 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; inum_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), + }; +} 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 +#include +#include +#include +#include "libusb.h" +#if 1 == BLADERF_OS_FREEBSD +#include +#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), +}; diff --git a/Radio/HW/BladeRF/src/backend/usb/nios_access.c b/Radio/HW/BladeRF/src/backend/usb/nios_access.c new file mode 100644 index 0000000..354d80b --- /dev/null +++ b/Radio/HW/BladeRF/src/backend/usb/nios_access.c @@ -0,0 +1,1322 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2015 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 +#include +#include +#include +#include + +#include "log.h" +#include "conversions.h" + +#include "usb.h" +#include "nios_access.h" +#include "nios_pkt_formats.h" + +#include "board/board.h" +#include "helpers/version.h" + +#if 0 +static void print_buf(const char *msg, const uint8_t *buf, size_t len) +{ + size_t i; + if (msg != NULL) { + fputs(msg, stderr); + } + + for (i = 0; i < len; i++) { + fprintf(stderr, " %02x", buf[i]); + } + + fputc('\n', stderr); +} +#else +#define print_buf(msg, data, len) do {} while(0) +#endif + +/* Buf is assumed to be NIOS_PKT_LEN bytes */ +static int nios_access(struct bladerf *dev, uint8_t *buf) +{ + struct bladerf_usb *usb = dev->backend_data; + int status; + + print_buf("NIOS II REQ:", buf, NIOS_PKT_LEN); + + /* Send the command */ + status = usb->fn->bulk_transfer(usb->driver, PERIPHERAL_EP_OUT, buf, + NIOS_PKT_LEN, PERIPHERAL_TIMEOUT_MS); + if (status != 0) { + log_error("Failed to send NIOS II request: %s\n", + bladerf_strerror(status)); + return status; + } + + /* Retrieve the request */ + status = usb->fn->bulk_transfer(usb->driver, PERIPHERAL_EP_IN, buf, + NIOS_PKT_LEN, PERIPHERAL_TIMEOUT_MS); + if (status != 0) { + log_error("Failed to receive NIOS II response: %s\n", + bladerf_strerror(status)); + } + + print_buf("NIOS II res:", buf, NIOS_PKT_LEN); + + return status; +} + +/* Variant that doesn't output to log_error on error. */ +static int nios_access_quiet(struct bladerf *dev, uint8_t *buf) +{ + struct bladerf_usb *usb = dev->backend_data; + int status; + + print_buf("NIOS II REQ:", buf, NIOS_PKT_LEN); + + /* Send the command */ + status = usb->fn->bulk_transfer(usb->driver, PERIPHERAL_EP_OUT, buf, + NIOS_PKT_LEN, PERIPHERAL_TIMEOUT_MS); + if (status != 0) { + return status; + } + + /* Retrieve the request */ + status = usb->fn->bulk_transfer(usb->driver, PERIPHERAL_EP_IN, buf, + NIOS_PKT_LEN, PERIPHERAL_TIMEOUT_MS); + + print_buf("NIOS II res:", buf, NIOS_PKT_LEN); + + return status; +} + +static int nios_8x8_read(struct bladerf *dev, uint8_t id, + uint8_t addr, uint8_t *data) +{ + int status; + uint8_t buf[NIOS_PKT_LEN]; + bool success; + + nios_pkt_8x8_pack(buf, id, false, addr, 0); + + status = nios_access(dev, buf); + if (status != 0) { + return status; + } + + nios_pkt_8x8_resp_unpack(buf, NULL, NULL, NULL, data, &success); + + if (success) { + return 0; + } else { + log_debug("%s: response packet reported failure.\n", __FUNCTION__); + *data = 0; + return BLADERF_ERR_FPGA_OP; + } +} + +static int nios_8x8_write(struct bladerf *dev, uint8_t id, + uint8_t addr, uint8_t data) +{ + int status; + uint8_t buf[NIOS_PKT_LEN]; + bool success; + + nios_pkt_8x8_pack(buf, id, true, addr, data); + + status = nios_access(dev, buf); + if (status != 0) { + return status; + } + + nios_pkt_8x8_resp_unpack(buf, NULL, NULL, NULL, NULL, &success); + + if (success) { + return 0; + } else { + log_debug("%s: response packet reported failure.\n", __FUNCTION__); + return BLADERF_ERR_FPGA_OP; + } +} + +static int nios_8x16_read(struct bladerf *dev, uint8_t id, + uint8_t addr, uint16_t *data) +{ + int status; + uint8_t buf[NIOS_PKT_LEN]; + bool success; + uint16_t tmp; + + nios_pkt_8x16_pack(buf, id, false, addr, 0); + + status = nios_access(dev, buf); + if (status != 0) { + return status; + } + + nios_pkt_8x16_resp_unpack(buf, NULL, NULL, NULL, &tmp, &success); + + if (success) { + *data = tmp; + return 0; + } else { + *data = 0; + log_debug("%s: response packet reported failure.\n", __FUNCTION__); + return BLADERF_ERR_FPGA_OP; + } +} + +static int nios_8x16_write(struct bladerf *dev, uint8_t id, + uint8_t addr, uint16_t data) +{ + int status; + uint8_t buf[NIOS_PKT_LEN]; + bool success; + + nios_pkt_8x16_pack(buf, id, true, addr, data); + + status = nios_access(dev, buf); + if (status != 0) { + return status; + } + + nios_pkt_8x16_resp_unpack(buf, NULL, NULL, NULL, NULL, &success); + + if (success) { + return 0; + } else { + log_debug("%s: response packet reported failure.\n", __FUNCTION__); + return BLADERF_ERR_FPGA_OP; + } +} + +static void log_debug_8x32_pkt(const uint8_t *buf) { + if (buf == NULL) { + log_debug("Failed to log packet: packet buffer is NULL\n"); + return; + } + + uint8_t target_id; + bool write; + uint8_t addr; + uint32_t data; + bool success; + + nios_pkt_8x32_resp_unpack(buf, &target_id, &write, &addr, &data, &success); + + const char *operation = write ? "Write" : "Read"; + const char *status = success ? "Success" : "Failure"; + + log_debug("Packet Magic: 0x%02x\n", buf[NIOS_PKT_8x32_IDX_MAGIC]); + log_debug("Packet Target: %s\n", target2str(target_id)); + log_debug("Packet Operation: %s\n", operation); + log_debug("Packet Address: 0x%02x\n", addr); + log_debug("Packet Data: 0x%08x\n", data); + log_debug("Packet Status: %s\n", status); +} + +static int nios_8x32_read(struct bladerf *dev, uint8_t id, + uint8_t addr, uint32_t *data) +{ + int status; + uint8_t buf[NIOS_PKT_LEN]; + bool success; + + nios_pkt_8x32_pack(buf, id, false, addr, 0); + + status = nios_access(dev, buf); + if (status != 0) { + return status; + } + + nios_pkt_8x32_resp_unpack(buf, NULL, NULL, NULL, data, &success); + + if (success) { + return 0; + } else { + *data = 0; + log_debug("%s: response packet reported failure.\n", __FUNCTION__); + log_debug_8x32_pkt(buf); + return BLADERF_ERR_FPGA_OP; + } +} + +static int nios_8x32_write(struct bladerf *dev, uint8_t id, + uint8_t addr, uint32_t data) +{ + int status; + uint8_t buf[NIOS_PKT_LEN]; + bool success; + + nios_pkt_8x32_pack(buf, id, true, addr, data); + + status = nios_access(dev, buf); + if (status != 0) { + return status; + } + + nios_pkt_8x32_resp_unpack(buf, NULL, NULL, NULL, NULL, &success); + + if (success) { + return 0; + } else { + log_debug("%s: response packet reported failure.\n", __FUNCTION__); + log_debug_8x32_pkt(buf); + return BLADERF_ERR_FPGA_OP; + } +} + +static int nios_16x64_read(struct bladerf *dev, + uint8_t id, + uint16_t addr, + uint64_t *data) +{ + int status; + uint8_t buf[NIOS_PKT_LEN]; + bool success; + + nios_pkt_16x64_pack(buf, id, false, addr, 0); + + /* RFIC access times out occasionally, and this is fine. */ + if (NIOS_PKT_16x64_TARGET_RFIC == id) { + status = nios_access_quiet(dev, buf); + } else { + status = nios_access(dev, buf); + } + + if (status != 0) { + return status; + } + + nios_pkt_16x64_resp_unpack(buf, NULL, NULL, NULL, data, &success); + + if (success) { + return 0; + } else { + *data = 0; + log_debug("%s: response packet reported failure.\n", __FUNCTION__); + return BLADERF_ERR_FPGA_OP; + } +} + +static int nios_16x64_write(struct bladerf *dev, + uint8_t id, + uint16_t addr, + uint64_t data) +{ + int status; + uint8_t buf[NIOS_PKT_LEN]; + bool success; + + nios_pkt_16x64_pack(buf, id, true, addr, data); + + /* RFIC access times out occasionally, and this is fine. */ + if (NIOS_PKT_16x64_TARGET_RFIC == id) { + status = nios_access_quiet(dev, buf); + } else { + status = nios_access(dev, buf); + } + + if (status != 0) { + return status; + } + + nios_pkt_16x64_resp_unpack(buf, NULL, NULL, NULL, NULL, &success); + + if (success) { + return 0; + } else { + log_debug("%s: response packet reported failure.\n", __FUNCTION__); + return BLADERF_ERR_FPGA_OP; + } +} + +static int nios_32x32_read(struct bladerf *dev, uint32_t id, + uint32_t addr, uint32_t *data) +{ + int status; + uint8_t buf[NIOS_PKT_LEN]; + bool success; + + nios_pkt_32x32_pack(buf, id, false, addr, 0); + + status = nios_access(dev, buf); + if (status != 0) { + return status; + } + + nios_pkt_32x32_resp_unpack(buf, NULL, NULL, NULL, data, &success); + + if (success) { + return 0; + } else { + *data = 0; + log_debug("%s: response packet reported failure.\n", __FUNCTION__); + return BLADERF_ERR_FPGA_OP; + } +} + +static int nios_32x32_write(struct bladerf *dev, uint32_t id, + uint32_t addr, uint32_t data) +{ + int status; + uint8_t buf[NIOS_PKT_LEN]; + bool success; + + nios_pkt_32x32_pack(buf, id, true, addr, data); + + status = nios_access(dev, buf); + if (status != 0) { + return status; + } + + nios_pkt_32x32_resp_unpack(buf, NULL, NULL, NULL, NULL, &success); + + if (success) { + return 0; + } else { + log_debug("%s: response packet reported failure.\n", __FUNCTION__); + return BLADERF_ERR_FPGA_OP; + } +} + +static int nios_32x32_masked_read(struct bladerf *dev, uint8_t id, + uint32_t mask, uint32_t *val) +{ + int status; + bool success; + uint8_t buf[NIOS_PKT_LEN]; + + /* The address is used as a mask of bits to read and return */ + nios_pkt_32x32_pack(buf, id, false, mask, 0); + + status = nios_access(dev, buf); + if (status != 0) { + return status; + } + + nios_pkt_32x32_resp_unpack(buf, NULL, NULL, NULL, val, &success); + + if (success) { + return 0; + } else { + *val = 0; + log_debug("%s: response packet reported failure.\n", __FUNCTION__); + return BLADERF_ERR_FPGA_OP; + } +} + +static int nios_32x32_masked_write(struct bladerf *dev, uint8_t id, + uint32_t mask, uint32_t val) +{ + int status; + bool success; + uint8_t buf[NIOS_PKT_LEN]; + + /* The address is used as a mask of bits to read and return */ + nios_pkt_32x32_pack(buf, id, true, mask, val); + + status = nios_access(dev, buf); + if (status != 0) { + return status; + } + + nios_pkt_32x32_resp_unpack(buf, NULL, NULL, NULL, NULL, &success); + + if (success) { + return 0; + } else { + log_debug("%s: response packet reported failure.\n", __FUNCTION__); + return BLADERF_ERR_FPGA_OP; + } +} + +int nios_config_read(struct bladerf *dev, uint32_t *val) +{ + int status = nios_8x32_read(dev, NIOS_PKT_8x32_TARGET_CONTROL, 0, val); + + if (status == 0) { + log_verbose("%s: Read 0x%08x\n", __FUNCTION__, *val); + } + + return status; +} + +int nios_config_write(struct bladerf *dev, uint32_t val) +{ + int status = nios_8x32_write(dev, NIOS_PKT_8x32_TARGET_CONTROL, 0, val); + + if (status == 0) { + log_verbose("%s: Wrote 0x%08x\n", __FUNCTION__, val); + } else { + log_error("%s: Failed to write 0x%08x\n", __FUNCTION__, val); + } + + return status; +} + +int nios_get_fpga_version(struct bladerf *dev, struct bladerf_version *ver) +{ + uint32_t regval; + int status = nios_8x32_read(dev, NIOS_PKT_8x32_TARGET_VERSION, 0, ®val); + + if (status == 0) { + log_verbose("%s: Read FPGA version word: 0x%08x\n", + __FUNCTION__, regval); + + ver->major = (regval >> 24) & 0xff; + ver->minor = (regval >> 16) & 0xff; + ver->patch = LE16_TO_HOST(regval & 0xffff); + + snprintf((char*)ver->describe, BLADERF_VERSION_STR_MAX, + "%d.%d.%d", ver->major, ver->minor, ver->patch); + + return 0; + } + + return status; +} + +int nios_get_timestamp(struct bladerf *dev, + bladerf_direction dir, + uint64_t *timestamp) +{ + int status; + uint8_t buf[NIOS_PKT_LEN]; + uint8_t addr; + bool success; + + switch (dir) { + case BLADERF_RX: + addr = NIOS_PKT_8x64_TIMESTAMP_RX; + break; + + case BLADERF_TX: + addr = NIOS_PKT_8x64_TIMESTAMP_TX; + break; + + default: + log_debug("Invalid direction: %d\n", dir); + return BLADERF_ERR_INVAL; + } + + nios_pkt_8x64_pack(buf, NIOS_PKT_8x64_TARGET_TIMESTAMP, false, addr, 0); + + status = nios_access(dev, buf); + if (status != 0) { + return status; + } + + nios_pkt_8x64_resp_unpack(buf, NULL, NULL, NULL, timestamp, &success); + + if (success) { + log_verbose("%s: Read %s timestamp: %" PRIu64 "\n", __FUNCTION__, + direction2str(dir), *timestamp); + return 0; + } else { + log_debug("%s: response packet reported failure.\n", __FUNCTION__); + *timestamp = 0; + return BLADERF_ERR_FPGA_OP; + } +} + +int nios_si5338_read(struct bladerf *dev, uint8_t addr, uint8_t *data) +{ + int status = nios_8x8_read(dev, NIOS_PKT_8x8_TARGET_SI5338, addr, data); + +#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE + if (status == 0) { + log_verbose("%s: Read 0x%02x from addr 0x%02x\n", + __FUNCTION__, *data, addr); + } +#endif + + return status; +} + +int nios_si5338_write(struct bladerf *dev, uint8_t addr, uint8_t data) +{ + int status = nios_8x8_write(dev, NIOS_PKT_8x8_TARGET_SI5338, addr, data); + +#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE + if (status == 0) { + log_verbose("%s: Wrote 0x%02x to addr 0x%02x\n", + __FUNCTION__, data, addr); + } +#endif + + return status; +} + +int nios_lms6_read(struct bladerf *dev, uint8_t addr, uint8_t *data) +{ + int status = nios_8x8_read(dev, NIOS_PKT_8x8_TARGET_LMS6, addr, data); + +#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE + if (status == 0) { + log_verbose("%s: Read 0x%02x from addr 0x%02x\n", + __FUNCTION__, *data, addr); + } +#endif + + return status; +} + +int nios_lms6_write(struct bladerf *dev, uint8_t addr, uint8_t data) +{ + int status = nios_8x8_write(dev, NIOS_PKT_8x8_TARGET_LMS6, addr, data); + +#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE + if (status == 0) { + log_verbose("%s: Wrote 0x%02x to addr 0x%02x\n", + __FUNCTION__, data, addr); + } +#endif + + return status; +} + +int nios_ina219_read(struct bladerf *dev, uint8_t addr, uint16_t *data) +{ + int status; + + status = nios_8x16_read(dev, NIOS_PKT_8x16_TARGET_INA219, addr, data); + if (status == 0) { + log_verbose("%s: Read 0x%04x from addr 0x%02x\n", + __FUNCTION__, *data, addr); + } + + return status; +} + +int nios_ina219_write(struct bladerf *dev, uint8_t addr, uint16_t data) +{ + int status; + + status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_INA219, addr, data); + if (status == 0) { + log_verbose("%s: Wrote 0x%04x to addr 0x%02x\n", + __FUNCTION__, data, addr); + } + + return status; +} + +#define VERBOSE_OUT_SINGLEBYTE "%s: %s 0x%02x @ addr 0x%04x\n" +#define VERBOSE_OUT_MULTIBYTE "%s: %s 0x%02x @ addr 0x%04x (%d/%d)\n" + +int nios_ad9361_spi_read(struct bladerf *dev, uint16_t cmd, uint64_t *data) +{ + int status; + + status = nios_16x64_read(dev, NIOS_PKT_16x64_TARGET_AD9361, cmd, data); + +#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE + if (log_get_verbosity() == BLADERF_LOG_LEVEL_VERBOSE && status == 0) { + size_t bytes = (((cmd >> 12) & 0x7) + 1); + size_t addr = cmd & 0xFFF; + + if (bytes > 1) { + size_t i; + for (i = 0; i < bytes; ++i) { + uint8_t byte = (*data >> (56 - 8 * i)) & 0xFF; + log_verbose(VERBOSE_OUT_MULTIBYTE, "ad9361_spi", " MRead", byte, + addr - i, i + 1, bytes); + } + } else { + uint8_t byte = (*data >> 56) & 0xFF; + log_verbose(VERBOSE_OUT_SINGLEBYTE, "ad9361_spi", " Read", byte, + addr); + } + } +#endif + + return status; +} + +int nios_ad9361_spi_write(struct bladerf *dev, uint16_t cmd, uint64_t data) +{ + int status; + + status = nios_16x64_write(dev, NIOS_PKT_16x64_TARGET_AD9361, cmd, data); + +#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE + if (log_get_verbosity() == BLADERF_LOG_LEVEL_VERBOSE && status == 0) { + size_t bytes = (((cmd >> 12) & 0x7) + 1) & 0xFF; + size_t addr = cmd & 0xFFF; + + if (bytes > 1) { + size_t i; + for (i = 0; i < bytes; ++i) { + uint8_t byte = (data >> (56 - 8 * i)) & 0xFF; + log_verbose(VERBOSE_OUT_MULTIBYTE, "ad9361_spi", "MWrite", byte, + addr - i, i + 1, bytes); + } + } else { + uint8_t byte = (data >> 56) & 0xFF; + log_verbose(VERBOSE_OUT_SINGLEBYTE, "ad9361_spi", " Write", byte, + addr); + } + } +#endif + + return status; +} + +int nios_adi_axi_read(struct bladerf *dev, uint32_t addr, uint32_t *data) +{ + int status; + + status = nios_32x32_read(dev, NIOS_PKT_32x32_TARGET_ADI_AXI, addr, data); + +#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE + if (status == 0) { + log_verbose("%s: Read 0x%08" PRIx32 " from addr 0x%04" PRIx32 "\n", + __FUNCTION__, *data, addr); + } +#endif + + return status; +} + +int nios_adi_axi_write(struct bladerf *dev, uint32_t addr, uint32_t data) +{ + int status; + + status = nios_32x32_write(dev, NIOS_PKT_32x32_TARGET_ADI_AXI, addr, data); + +#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE + if (status == 0) { + log_verbose("%s: Wrote 0x%08" PRIx32 " to addr 0x%04" PRIx32 "\n", + __FUNCTION__, data, addr); + } +#endif + + return status; +} + +int nios_wishbone_master_read(struct bladerf *dev, uint32_t addr, uint32_t *data) +{ + int status; + + status = nios_32x32_read(dev, NIOS_PKT_32x32_TARGET_WB_MSTR, addr, data); + +#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE + if (status == 0) { + log_verbose("%s: Read 0x%08" PRIx32 " from addr 0x%04" PRIx32 "\n", + __FUNCTION__, *data, addr); + } +#endif + + return status; +} + +int nios_wishbone_master_write(struct bladerf *dev, uint32_t addr, uint32_t data) +{ + int status; + + status = nios_32x32_write(dev, NIOS_PKT_32x32_TARGET_WB_MSTR, addr, data); + +#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE + if (status == 0) { + log_verbose("%s: Wrote 0x%08" PRIx32 " to addr 0x%04" PRIx32 "\n", + __FUNCTION__, data, addr); + } +#endif + + return status; +} + +int nios_rfic_command_read(struct bladerf *dev, uint16_t cmd, uint64_t *data) +{ + int status; + + status = nios_16x64_read(dev, NIOS_PKT_16x64_TARGET_RFIC, cmd, data); + +#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE + if (status == 0) { + log_verbose("%s: Read 0x%04x 0x%08x\n", __FUNCTION__, cmd, *data); + } +#endif + + return status; +} + +int nios_rfic_command_write(struct bladerf *dev, uint16_t cmd, uint64_t data) +{ + int status; + + status = nios_16x64_write(dev, NIOS_PKT_16x64_TARGET_RFIC, cmd, data); + +#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE + if (status == 0) { + log_verbose("%s: Write 0x%04x 0x%08x\n", __FUNCTION__, cmd, data); + } +#endif + + return status; +} + +int nios_rffe_control_read(struct bladerf *dev, uint32_t *value) +{ + int status; + + status = nios_8x32_read(dev, NIOS_PKT_8x32_TARGET_RFFE_CSR, 0, value); + +#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE + if (status == 0) { + log_verbose("%s: Read 0x%08x\n", __FUNCTION__, *value); + } +#endif + + return status; +} + +int nios_rffe_control_write(struct bladerf *dev, uint32_t value) +{ + int status; + + status = nios_8x32_write(dev, NIOS_PKT_8x32_TARGET_RFFE_CSR, 0, value); + +#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE + if (status == 0) { + log_verbose("%s: Wrote 0x%08x\n", __FUNCTION__, value); + } +#endif + + return status; +} + +int nios_rffe_fastlock_save(struct bladerf *dev, bool is_tx, + uint8_t rffe_profile, uint16_t nios_profile) +{ + int status; + uint8_t addr; + uint32_t data = 0; + + addr = is_tx ? 1 : 0; + data = (rffe_profile << 16) | nios_profile; + + status = nios_8x32_write(dev, NIOS_PKT_8x32_TARGET_FASTLOCK, addr, data); + +#ifdef ENABLE_LIBBLADERF_NIOS_ACCESS_LOG_VERBOSE + if (status == 0) { + log_verbose("%s: Wrote 0x%08x\n", __FUNCTION__, data); + } +#endif + + return status; +} + +int nios_ad56x1_vctcxo_trim_dac_read(struct bladerf *dev, uint16_t *value) +{ + int status; + + status = nios_8x16_read(dev, NIOS_PKT_8x16_TARGET_AD56X1_DAC, 0, value); + if (status == 0) { + log_verbose("%s: Read 0x%04x\n", __FUNCTION__, *value); + } + + return status; +} + +int nios_ad56x1_vctcxo_trim_dac_write(struct bladerf *dev, uint16_t value) +{ + int status; + + status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_AD56X1_DAC, 0, value); + if (status == 0) { + log_verbose("%s: Wrote 0x%04x\n", __FUNCTION__, value); + } + + return status; +} + +int nios_adf400x_read(struct bladerf *dev, uint8_t addr, uint32_t *data) +{ + int status; + + status = nios_8x32_read(dev, NIOS_PKT_8x32_TARGET_ADF400X, addr, data); + if (status == 0) { + log_verbose("%s: Read 0x%08x from addr 0x%02x\n", __FUNCTION__, *data, addr); + } + + return status; +} + +int nios_adf400x_write(struct bladerf *dev, uint8_t addr, uint32_t data) +{ + int status; + + data &= ~(0x3); + + status = nios_8x32_write(dev, NIOS_PKT_8x32_TARGET_ADF400X, 0, data | (addr & 0x3)); + if (status == 0) { + log_verbose("%s: Wrote 0x%08x to addr 0x%02x\n", __FUNCTION__, data, addr); + } + + return status; +} + +int nios_vctcxo_trim_dac_write(struct bladerf *dev, uint8_t addr, uint16_t value) +{ + return nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_VCTCXO_DAC, addr, value); +} + +int nios_vctcxo_trim_dac_read(struct bladerf *dev, uint8_t addr, uint16_t *value) +{ + return nios_8x16_read(dev, NIOS_PKT_8x16_TARGET_VCTCXO_DAC, addr, value); +} + +int nios_set_vctcxo_tamer_mode(struct bladerf *dev, + bladerf_vctcxo_tamer_mode mode) +{ + int status; + + status = nios_8x8_write(dev, + NIOS_PKT_8x8_TARGET_VCTCXO_TAMER, 0xff, + (uint8_t) mode); + + if (status == 0) { + log_verbose("%s: Wrote mode=0x%02x\n", __FUNCTION__, mode); + } + + return status; +} + +int nios_get_vctcxo_tamer_mode(struct bladerf *dev, + bladerf_vctcxo_tamer_mode *mode) +{ + int status; + uint8_t tmp; + + *mode = BLADERF_VCTCXO_TAMER_INVALID; + + status = nios_8x8_read(dev, NIOS_PKT_8x8_TARGET_VCTCXO_TAMER, 0xff, &tmp); + if (status == 0) { + log_verbose("%s: Read mode=0x%02x\n", __FUNCTION__, tmp); + + switch ((bladerf_vctcxo_tamer_mode) tmp) { + case BLADERF_VCTCXO_TAMER_DISABLED: + case BLADERF_VCTCXO_TAMER_1_PPS: + case BLADERF_VCTCXO_TAMER_10_MHZ: + *mode = (bladerf_vctcxo_tamer_mode) tmp; + break; + + default: + status = BLADERF_ERR_UNEXPECTED; + } + } + + return status; +} + + +int nios_get_iq_gain_correction(struct bladerf *dev, bladerf_channel ch, + int16_t *value) +{ + int status = BLADERF_ERR_INVAL; + uint16_t tmp = 0; + + switch (ch) { + case BLADERF_CHANNEL_RX(0): + status = nios_8x16_read(dev, NIOS_PKT_8x16_TARGET_IQ_CORR, + NIOS_PKT_8x16_ADDR_IQ_CORR_RX_GAIN, &tmp); + break; + + case BLADERF_CHANNEL_TX(0): + status = nios_8x16_read(dev, NIOS_PKT_8x16_TARGET_IQ_CORR, + NIOS_PKT_8x16_ADDR_IQ_CORR_TX_GAIN, &tmp); + break; + + default: + log_debug("Invalid channel: 0x%x\n", ch); + } + + *value = (int16_t) tmp; + + if (status == 0) { + log_verbose("%s: Read %s %d\n", + __FUNCTION__, channel2str(ch), *value); + } + + return status; +} + +int nios_get_iq_phase_correction(struct bladerf *dev, bladerf_channel ch, + int16_t *value) +{ + int status = BLADERF_ERR_INVAL; + uint16_t tmp = 0; + + switch (ch) { + case BLADERF_CHANNEL_RX(0): + status = nios_8x16_read(dev, NIOS_PKT_8x16_TARGET_IQ_CORR, + NIOS_PKT_8x16_ADDR_IQ_CORR_RX_PHASE, &tmp); + break; + + case BLADERF_CHANNEL_TX(0): + status = nios_8x16_read(dev, NIOS_PKT_8x16_TARGET_IQ_CORR, + NIOS_PKT_8x16_ADDR_IQ_CORR_TX_PHASE, &tmp); + break; + + default: + log_debug("Invalid channel: 0x%x\n", ch); + } + + *value = (int16_t) tmp; + + if (status == 0) { + log_verbose("%s: Read %s %d\n", + __FUNCTION__, channel2str(ch), *value); + } + + return status; +} + +int nios_set_iq_gain_correction(struct bladerf *dev, bladerf_channel ch, + int16_t value) +{ + int status = BLADERF_ERR_INVAL; + + switch (ch) { + case BLADERF_CHANNEL_RX(0): + log_verbose("Setting RX IQ Correction gain: %d\n", value); + status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_IQ_CORR, + NIOS_PKT_8x16_ADDR_IQ_CORR_RX_GAIN, value); + break; + + case BLADERF_CHANNEL_TX(0): + log_verbose("Setting TX IQ Correction gain: %d\n", value); + status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_IQ_CORR, + NIOS_PKT_8x16_ADDR_IQ_CORR_TX_GAIN, value); + break; + + default: + log_debug("Invalid channel: 0x%x\n", ch); + } + + if (status == 0) { + log_verbose("%s: Wrote %s %d\n", + __FUNCTION__, channel2str(ch), value); + } + + return status; +} + +int nios_set_iq_phase_correction(struct bladerf *dev, bladerf_channel ch, + int16_t value) +{ + int status = BLADERF_ERR_INVAL; + + switch (ch) { + case BLADERF_CHANNEL_RX(0): + log_verbose("Setting RX IQ Correction phase: %d\n", value); + status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_IQ_CORR, + NIOS_PKT_8x16_ADDR_IQ_CORR_RX_PHASE, value); + break; + + case BLADERF_CHANNEL_TX(0): + log_verbose("Setting TX IQ Correction phase: %d\n", value); + status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_IQ_CORR, + NIOS_PKT_8x16_ADDR_IQ_CORR_TX_PHASE, value); + break; + + default: + log_debug("Invalid channel: 0x%x\n", ch); + } + + if (status == 0) { + log_verbose("%s: Wrote %s %d\n", + __FUNCTION__, channel2str(ch), value); + } + + + return status; +} + +int nios_set_agc_dc_correction(struct bladerf *dev, int16_t q_max, int16_t i_max, + int16_t q_mid, int16_t i_mid, + int16_t q_low, int16_t i_low) +{ + int status; + + status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_AGC_CORR, NIOS_PKT_8x16_ADDR_AGC_DC_Q_MAX, q_max); + + if (!status) + status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_AGC_CORR, NIOS_PKT_8x16_ADDR_AGC_DC_I_MAX, i_max); + if (!status) + status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_AGC_CORR, NIOS_PKT_8x16_ADDR_AGC_DC_Q_MID, q_mid); + if (!status) + status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_AGC_CORR, NIOS_PKT_8x16_ADDR_AGC_DC_I_MID, i_mid); + if (!status) + status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_AGC_CORR, NIOS_PKT_8x16_ADDR_AGC_DC_Q_MIN, q_low); + if (!status) + status = nios_8x16_write(dev, NIOS_PKT_8x16_TARGET_AGC_CORR, NIOS_PKT_8x16_ADDR_AGC_DC_I_MIN, i_low); + + return status; +} + +int nios_xb200_synth_write(struct bladerf *dev, uint32_t value) +{ + int status = nios_8x32_write(dev, NIOS_PKT_8x32_TARGET_ADF4351, 0, value); + + if (status == 0) { + log_verbose("%s: Wrote 0x%08x\n", __FUNCTION__, value); + } + + return status; +} + +int nios_expansion_gpio_read(struct bladerf *dev, uint32_t *val) +{ + int status = nios_32x32_masked_read(dev, NIOS_PKT_32x32_TARGET_EXP, + 0xffffffff, val); + + if (status == 0) { + log_verbose("%s: Read 0x%08x\n", __FUNCTION__, *val); + } + + return status; +} + +int nios_expansion_gpio_write(struct bladerf *dev, uint32_t mask, uint32_t val) +{ + int status = nios_32x32_masked_write(dev, NIOS_PKT_32x32_TARGET_EXP, + mask, val); + + if (status == 0) { + log_verbose("%s: Wrote 0x%08x (with mask 0x%08x)\n", + __FUNCTION__, val, mask); + } + + return status; +} + +int nios_expansion_gpio_dir_read(struct bladerf *dev, uint32_t *val) +{ + int status = nios_32x32_masked_read(dev, NIOS_PKT_32x32_TARGET_EXP_DIR, + 0xffffffff, val); + + if (status == 0) { + log_verbose("%s: Read 0x%08x\n", __FUNCTION__, *val); + } + + return status; +} + +int nios_expansion_gpio_dir_write(struct bladerf *dev, + uint32_t mask, uint32_t val) +{ + int status = nios_32x32_masked_write(dev, NIOS_PKT_32x32_TARGET_EXP_DIR, + mask, val); + + if (status == 0) { + log_verbose("%s: Wrote 0x%08x (with mask 0x%08x)\n", + __FUNCTION__, val, mask); + } + + return status; +} + +int nios_retune(struct bladerf *dev, bladerf_channel ch, + uint64_t timestamp, uint16_t nint, uint32_t nfrac, + uint8_t freqsel, uint8_t vcocap, bool low_band, + uint8_t xb_gpio, bool quick_tune) +{ + int status; + uint8_t buf[NIOS_PKT_LEN]; + + uint8_t resp_flags; + uint64_t duration; + + if (timestamp == NIOS_PKT_RETUNE_CLEAR_QUEUE) { + log_verbose("Clearing %s retune queue.\n", channel2str(ch)); + } else { + log_verbose("%s: channel=%s timestamp=%"PRIu64" nint=%u nfrac=%u\n\t\t\t\t" + "freqsel=0x%02x vcocap=0x%02x low_band=%d quick_tune=%d\n", + __FUNCTION__, channel2str(ch), timestamp, nint, nfrac, + freqsel, vcocap, low_band, quick_tune); + } + + nios_pkt_retune_pack(buf, ch, timestamp, + nint, nfrac, freqsel, vcocap, low_band, + xb_gpio, quick_tune); + + status = nios_access(dev, buf); + if (status != 0) { + return status; + } + + nios_pkt_retune_resp_unpack(buf, &duration, &vcocap, &resp_flags); + + if (resp_flags & NIOS_PKT_RETUNERESP_FLAG_TSVTUNE_VALID) { + log_verbose("%s retune operation: vcocap=%u, duration=%"PRIu64"\n", + channel2str(ch), vcocap, duration); + } else { + log_verbose("%s operation duration: %"PRIu64"\n", + channel2str(ch), duration); + } + + if ((resp_flags & NIOS_PKT_RETUNERESP_FLAG_SUCCESS) == 0) { + if (timestamp == BLADERF_RETUNE_NOW) { + log_debug("FPGA tuning reported failure.\n"); + status = BLADERF_ERR_UNEXPECTED; + } else { + log_debug("The FPGA's retune queue is full. Try again after " + "a previous request has completed.\n"); + status = BLADERF_ERR_QUEUE_FULL; + } + } + + return status; +} + +int nios_retune2(struct bladerf *dev, bladerf_channel ch, + uint64_t timestamp, uint16_t nios_profile, + uint8_t rffe_profile, uint8_t port, + uint8_t spdt) +{ + int status; + uint8_t buf[NIOS_PKT_LEN]; + + uint8_t resp_flags; + uint64_t duration; + + if (timestamp == NIOS_PKT_RETUNE2_CLEAR_QUEUE) { + log_verbose("Clearing %s retune queue.\n", channel2str(ch)); + } else { + log_verbose("%s: channel=%s timestamp=%"PRIu64" nios_profile=%u " + "rffe_profile=%u\n\t\t\t\tport=0x%02x spdt=0x%02x\n", + __FUNCTION__, channel2str(ch), timestamp, nios_profile, + rffe_profile, port, spdt); + } + + nios_pkt_retune2_pack(buf, ch, timestamp, nios_profile, rffe_profile, + port, spdt); + + status = nios_access(dev, buf); + if (status != 0) { + return status; + } + + nios_pkt_retune2_resp_unpack(buf, &duration, &resp_flags); + + if (resp_flags & NIOS_PKT_RETUNE2_RESP_FLAG_TSVTUNE_VALID) { + log_verbose("%s retune operation: duration=%"PRIu64"\n", + channel2str(ch), duration); + } else { + log_verbose("%s operation duration: %"PRIu64"\n", + channel2str(ch), duration); + } + + if ((resp_flags & NIOS_PKT_RETUNE2_RESP_FLAG_SUCCESS) == 0) { + if (timestamp == BLADERF_RETUNE_NOW) { + log_debug("FPGA tuning reported failure.\n"); + status = BLADERF_ERR_UNEXPECTED; + } else { + log_debug("The FPGA's retune queue is full. Try again after " + "a previous request has completed.\n"); + status = BLADERF_ERR_QUEUE_FULL; + } + } + + return status; +} + +int nios_read_trigger(struct bladerf *dev, bladerf_channel ch, + bladerf_trigger_signal trigger, uint8_t *value) +{ + int status; + uint8_t nios_id; + + switch (ch) { + case BLADERF_CHANNEL_TX(0): + nios_id = NIOS_PKT_8x8_TX_TRIGGER_CTL; + break; + + case BLADERF_CHANNEL_RX(0): + nios_id = NIOS_PKT_8x8_RX_TRIGGER_CTL; + break; + + default: + log_debug("Invalid channel: 0x%x\n", ch); + return BLADERF_ERR_INVAL; + } + + /* Only 1 external trigger is currently supported */ + switch (trigger) { + case BLADERF_TRIGGER_J71_4: + case BLADERF_TRIGGER_J51_1: + case BLADERF_TRIGGER_MINI_EXP_1: + break; + + default: + log_debug("Invalid trigger: %d\n", trigger); + return BLADERF_ERR_INVAL; + } + + status = nios_8x8_read(dev, nios_id, 0, value); + if (status == 0) { + log_verbose("%s trigger read value 0x%02x\n", channel2str(ch), *value); + } + + return status; +} + +int nios_write_trigger(struct bladerf *dev, bladerf_channel ch, + bladerf_trigger_signal trigger, uint8_t value) +{ + int status; + uint8_t nios_id; + + switch (ch) { + case BLADERF_CHANNEL_TX(0): + nios_id = NIOS_PKT_8x8_TX_TRIGGER_CTL; + break; + + case BLADERF_CHANNEL_RX(0): + nios_id = NIOS_PKT_8x8_RX_TRIGGER_CTL; + break; + + default: + log_debug("Invalid channel: 0x%x\n", ch); + return BLADERF_ERR_INVAL; + } + + /* Only 1 external trigger is currently supported */ + switch (trigger) { + case BLADERF_TRIGGER_J71_4: + case BLADERF_TRIGGER_J51_1: + case BLADERF_TRIGGER_MINI_EXP_1: + break; + + default: + log_debug("Invalid trigger: %d\n", trigger); + return BLADERF_ERR_INVAL; + } + + status = nios_8x8_write(dev, nios_id, 0, value); + if (status == 0) { + log_verbose("%s trigger write value 0x%02x\n", channel2str(ch), value); + } + + return status; +} diff --git a/Radio/HW/BladeRF/src/backend/usb/nios_access.h b/Radio/HW/BladeRF/src/backend/usb/nios_access.h new file mode 100644 index 0000000..f02270e --- /dev/null +++ b/Radio/HW/BladeRF/src/backend/usb/nios_access.h @@ -0,0 +1,582 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2015 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 file defines functionality used to communicate with the NIOS II + * soft-core processor running on the FPGA versions >= v0.3.0 + * + * The host communicates with the NIOS II via USB transfers to the Cypress FX3, + * which then communicates with the FPGA via a UART. This module packs and + * submits the requests, and receives and unpacks the responses. + * + * See the files in the top-level fpga_common/ directory for description + * and macros pertaining to the legacy packet format. + */ + +#ifndef BACKEND_USB_NIOS_ACCESS_H_ +#define BACKEND_USB_NIOS_ACCESS_H_ + +#include +#include +#include + +#include "board/board.h" +#include "usb.h" + +/** + * Read from the FPGA's config register + * + * @param dev Device handle + * @param[out] val On success, updated with config register value + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_config_read(struct bladerf *dev, uint32_t *val); + +/** + * Write to FPGA's config register + * + * @param dev Device handle + * @param[in] val Register value to write + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_config_write(struct bladerf *dev, uint32_t val); + +/** + * Read the FPGA version into the provided version structure + * + * @param dev Device handle + * @param[out] ver Updated with FPGA version (upon success) + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_get_fpga_version(struct bladerf *dev, struct bladerf_version *ver); + +/** + * Read a timestamp counter value + * + * @param dev Device handle + * @param[in] dir Stream direction + * @param[out] timestamp On success, updated with the timestamp value + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_get_timestamp(struct bladerf *dev, + bladerf_direction dir, + uint64_t *timestamp); + +/** + * Read from an Si5338 register + * + * @param dev Device handle + * @param[in] addr Register address + * @param[out] data On success, updated with read data + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_si5338_read(struct bladerf *dev, uint8_t addr, uint8_t *data); + +/** + * Write to an Si5338 register + * + * @param dev Device handle + * @param[in] addr Register address + * @param[in] data Data to write + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_si5338_write(struct bladerf *dev, uint8_t addr, uint8_t data); + +/** + * Read from an LMS6002D register + * + * @param dev Device handle + * @param[in] addr Register address + * @param[out] data On success, updated with data read from the device + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_lms6_read(struct bladerf *dev, uint8_t addr, uint8_t *data); + +/** + * Write to an LMS6002D register + * + * @param dev Device handle + * @param[in] addr Register address + * @param[out] data Register data + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_lms6_write(struct bladerf *dev, uint8_t addr, uint8_t data); + +/** + * Read from an INA219 register + * + * @param dev Device handle + * @param[in] addr Register address + * @param[out] data Data + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_ina219_read(struct bladerf *dev, uint8_t addr, uint16_t *data); + +/** + * Write to an INA219 register + * + * @param dev Device handle + * @param[in] addr Register address + * @param[in] data Data + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_ina219_write(struct bladerf *dev, uint8_t addr, uint16_t data); + +/** + * Read the AD9361 over SPI. + * + * @param dev Device handle + * @param[in] cmd Command + * @param[out] data Data + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_ad9361_spi_read(struct bladerf *dev, uint16_t cmd, uint64_t *data); + +/** + * Write to the AD9361 over SPI. + * + * @param dev Device handle + * @param[in] cmd Command + * @param[in] data Data + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_ad9361_spi_write(struct bladerf *dev, uint16_t cmd, uint64_t data); + +/** + * Read the ADI AXI memory mapped region. + * + * @param dev Device handle + * @param[in] addr Address + * @param[out] data Data + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_adi_axi_read(struct bladerf *dev, uint32_t cmd, uint32_t *data); + +/** + * Write to the ADI AXI memory mapped region. + * + * @param dev Device handle + * @param[in] addr Address + * @param[in] data Data + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_adi_axi_write(struct bladerf *dev, uint32_t cmd, uint32_t data); + +/** + * Read the Wishbone Master memory mapped region. + * + * @param dev Device handle + * @param[in] addr Address + * @param[out] data Data + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_wishbone_master_read(struct bladerf *dev, uint32_t addr, uint32_t *data); + +/** + * Write to the Wishbone Master memory mapped region. + * + * @param dev Device handle + * @param[in] addr Address + * @param[in] data Data + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_wishbone_master_write(struct bladerf *dev, uint32_t addr, uint32_t data); + +/** + * Read RFIC command + * + * @param dev Device handle + * @param[in] cmd Command: `(command & 0xFF) + ((channel & 0xF) << 8)` + * @param[out] data Data + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_rfic_command_read(struct bladerf *dev, uint16_t cmd, uint64_t *data); + +/** + * Write RFIC command + * + * @param dev Device handle + * @param[in] cmd Command: `(command & 0xFF) + ((channel & 0xF) << 8)` + * @param[in] data Data + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_rfic_command_write(struct bladerf *dev, uint16_t cmd, uint64_t data); + +/** + * Read RFFE control register. + * + * @param dev Device handle + * @param[in] len Data buffer length + * @param[out] value Value + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_rffe_control_read(struct bladerf *dev, uint32_t *value); + +/** + * Write RFFE control register. + * + * @param dev Device handle + * @param[in] len Data buffer length + * @param[in] value Value + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_rffe_control_write(struct bladerf *dev, uint32_t value); + +/** + * Save an RFFE fast lock profile to the Nios. + * + * @param dev Device handle + * @param[in] is_tx True if TX profile, false if RX profile + * @param[in] rffe_profile RFFE profile to save + * @param[in] nios_profile Where to save the profile in the Nios + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_rffe_fastlock_save(struct bladerf *dev, bool is_tx, + uint8_t rffe_profile, uint16_t nios_profile); + +/** + * Write to the AD56X1 VCTCXO trim DAC. + * + * @param dev Device handle + * @param[in] value Value + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_ad56x1_vctcxo_trim_dac_write(struct bladerf *dev, uint16_t value); + +/** + * Read the AD56X1 VCTCXO trim DAC. + * + * @param dev Device handle + * @param[out] value Data + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_ad56x1_vctcxo_trim_dac_read(struct bladerf *dev, uint16_t *value); + +/** + * Write to the ADF400X. + * + * @param dev Device handle + * @param[in] addr Address + * @param[in] data Data + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_adf400x_write(struct bladerf *dev, uint8_t addr, uint32_t data); + +/** + * Read from the ADF400X. + * + * @param dev Device handle + * @param[in] addr Address + * @param[out] data Data + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_adf400x_read(struct bladerf *dev, uint8_t addr, uint32_t *data); + +/** + * Write to a VCTCXO trim DAC register. + * + * @param dev Device handle + * @param[in] addr Register + * @param[in] value Value + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_vctcxo_trim_dac_write(struct bladerf *dev, + uint8_t addr, + uint16_t value); + +/** + * Read from a VCTCXO trim DAC register. + * + * @param dev Device handle + * @param[in] addr Register + * @param[out] value Data + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_vctcxo_trim_dac_read(struct bladerf *dev, + uint8_t addr, + uint16_t *value); + +/** + * Write VCTCXO tamer mode selection + * + * @param dev Device handle + * @param[in] mode Tamer mode to use, or BLADERF_VCTCXO_TAMER_DISABLED + * to disable the tamer. + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_set_vctcxo_tamer_mode(struct bladerf *dev, + bladerf_vctcxo_tamer_mode mode); + +/** + * Read the current VCTCXO mode selection + * + * @param dev Device handle + * @param[out] mode Current tamer mode in use. + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_get_vctcxo_tamer_mode(struct bladerf *dev, + bladerf_vctcxo_tamer_mode *mode); + +/** + * Read a IQ gain correction value + * + * @param dev Device handle + * @param[in] ch Channel + * @param[out] value On success, updated with phase correction value + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_get_iq_gain_correction(struct bladerf *dev, + bladerf_channel ch, + int16_t *value); + +/** + * Read a IQ phase correction value + * + * @param dev Device handle + * @param[in] ch Channel + * @param[out] value On success, updated with phase correction value + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_get_iq_phase_correction(struct bladerf *dev, + bladerf_channel ch, + int16_t *value); + +/** + * Write an IQ gain correction value + * + * @param dev Device handle + * @param[in] ch Channel + * @param[in] value Correction value to write + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_set_iq_gain_correction(struct bladerf *dev, + bladerf_channel ch, + int16_t value); + +/** + * Write an IQ phase correction value + * + * @param dev Device handle + * @param[in] ch Channel + * @param[in] value Correction value to write + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_set_iq_phase_correction(struct bladerf *dev, + bladerf_channel ch, + int16_t value); + +/** + * Write AGC DC LUT values + * + * @param[in] dev Device handle + * @param[in] q_max Q DC offset at Max gain + * @param[in] i_max I DC offset at Max gain + * @param[in] q_mid Q DC offset at Mid gain + * @param[in] i_mid I DC offset at Mid gain + * @param[in] q_low Q DC offset at Low gain + * @param[in] i_low I DC offset at Low gain + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_set_agc_dc_correction(struct bladerf *dev, + int16_t q_max, + int16_t i_max, + int16_t q_mid, + int16_t i_mid, + int16_t q_low, + int16_t i_low); +/** + * Write a value to the XB-200's ADF4351 synthesizer + * + * @param dev Device handle + * @param[in] value Write value + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_xb200_synth_write(struct bladerf *dev, uint32_t value); + +/** + * Read from expanion GPIO + * + * @param dev Device handle + * @param[out] value On success, updated with read value + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_expansion_gpio_read(struct bladerf *dev, uint32_t *val); + +/** + * Write to expansion GPIO + * + * @param dev Device handle + * @param[in] mask Mask denoting bits to write + * @param[in] value Write value + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_expansion_gpio_write(struct bladerf *dev, uint32_t mask, uint32_t val); + +/** + * Read from expansion GPIO direction register + * + * @param dev Device handle + * @param[out] value On success, updated with read value + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_expansion_gpio_dir_read(struct bladerf *dev, uint32_t *val); + +/** + * Write to expansion GPIO direction register + * + * @param dev Device handle + * @param[in] mask Mask denoting bits to configure + * @param[in] output '1' bit denotes output, '0' bit denotes input + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_expansion_gpio_dir_write(struct bladerf *dev, + uint32_t mask, + uint32_t outputs); + +/** + * Dummy handler for a retune request, which is not supported on + * earlier FPGA versions. + * + * All of the following parameters are ignored. + * + * @param dev Device handle + * @param[in] ch Channel + * @param[in] timestamp Time to schedule retune at + * @param[in] nint Integer portion of frequency multiplier + * @param[in] nfrac Fractional portion of frequency multiplier + * @param[in] freqsel VCO and divider selection + * @param[in] low_band High vs low band selection + * @param[in] xb_gpio XB configuration bits + * @param[in] quick_tune Denotes quick tune should be used instead of + * tuning algorithm + * + * @return BLADERF_ERR_UNSUPPORTED + */ +int nios_retune(struct bladerf *dev, + bladerf_channel ch, + uint64_t timestamp, + uint16_t nint, + uint32_t nfrac, + uint8_t freqsel, + uint8_t vcocap, + bool low_band, + uint8_t xb_gpio, + bool quick_tune); + +/** + * Handler for a retune request on bladeRF2 devices. The RFFEs used in these + * devices have a concept called fast lock profiles that store all the VCO + * information needed to quickly retune to a different frequency. The RFFE + * can store up to 8 profiles for RX, and a separate 8 for TX. To use more + * profiles, they must be stored in the baseband processor (e.g. the FPGA + * or Nios) and loaded into one of the 8 slots as needed. + * + * Each of these profiles consumes 16 bytes not including the timestamp and + * RF port/switch information. This is too large to fit into a single Nios + * packet at this time, so to improve retune time, the profiles are stored + * in the Nios and the Host will schedule retunes by specifying the Nios + * profile to load into the specified RFFE profile slot. + * + * @param dev Device handle + * @param[in] ch Channel + * @param[in] timestamp Time to schedule retune at + * @param[in] nios_profile Nios profile number (0-N) to load into the RFFE + * @param[in] rffe_profile RFFE fast lock profile number (0-7) into which + * the Nios profile will be loaded. + * @param[in] port RFFE port settings + * @param[in] spdt RF SPDT switch settings + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_retune2(struct bladerf *dev, bladerf_channel ch, + uint64_t timestamp, uint16_t nios_profile, + uint8_t rffe_profile, uint8_t port, uint8_t spdt); + +/** + * Read trigger register value + * + * @param dev Device handle + * @param[in] ch Channel + * @param[in] trigger Trigger to read from + * @param[out] value On success, updated with register value + * + * + * @return 0 on success, BLADERF_ERR_* code on error + */ +int nios_read_trigger(struct bladerf *dev, + bladerf_channel ch, + bladerf_trigger_signal trigger, + uint8_t *value); + +/** + * Write trigger register value + * + * @param dev Device handle + * @param[in] ch Channel + * @param[in] trigger Trigger to read + * @param[in] value Value to write + * + * @return 0 on success, BLADERF_ERR_* code on error + */ +int nios_write_trigger(struct bladerf *dev, + bladerf_channel ch, + bladerf_trigger_signal trigger, + uint8_t value); + +#endif diff --git a/Radio/HW/BladeRF/src/backend/usb/nios_legacy_access.c b/Radio/HW/BladeRF/src/backend/usb/nios_legacy_access.c new file mode 100644 index 0000000..e4fe727 --- /dev/null +++ b/Radio/HW/BladeRF/src/backend/usb/nios_legacy_access.c @@ -0,0 +1,744 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2014-2015 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 file implements functionality used to communicate with the NIOS II + * soft-core processor running on the FPGA versions prior to v0.2.x. + * (Although post v0.2.x FPGAs support this for reverse compatibility). + * + * The host communicates with the NIOS II via USB transfers to the Cypress FX3, + * which then communicates with the FPGA via a UART. + * + * See the files in the top-level fpga_common/ directory for description + * and macros pertaining to the legacy packet format. + */ + +#include +#include +#include + +#include "log.h" + +#include "usb.h" +#include "nios_legacy_access.h" +#include "nios_pkt_formats.h" + +#include "board/board.h" +#include "board/bladerf1/capabilities.h" +#include "helpers/version.h" + +#include "board/bladerf1/capabilities.h" + +#if 0 +static void print_buf(const char *msg, const uint8_t *buf, size_t len) +{ + size_t i; + if (msg != NULL) { + fputs(msg, stderr); + } + + for (i = 0; i < len; i++) { + if (i == 0) { + fprintf(stderr, " 0x%02x,", buf[i]); + } else if ((i + 1) % 8 == 0) { + fprintf(stderr, " 0x%02x,\n ", buf[i]); + } else { + fprintf(stderr, " 0x%02x,", buf[i]); + } + } + + fputc('\n', stderr); +} +#else +#define print_buf(msg, data, len) do {} while(0) +#endif + +/* Access device/module via the legacy NIOS II packet format. */ +static int nios_access(struct bladerf *dev, uint8_t peripheral, + usb_direction dir, struct uart_cmd *cmd, + size_t len) +{ + struct bladerf_usb *usb = dev->backend_data; + + int status; + size_t i; + uint8_t buf[16] = { 0 }; + const uint8_t pkt_mode_dir = (dir == USB_DIR_HOST_TO_DEVICE) ? + NIOS_PKT_LEGACY_MODE_DIR_WRITE : + NIOS_PKT_LEGACY_MODE_DIR_READ; + + assert(len <= ((sizeof(buf) - 2) / 2)); + + /* Populate the buffer for transfer, given address data pairs */ + buf[0] = NIOS_PKT_LEGACY_MAGIC; + buf[1] = pkt_mode_dir | peripheral | (uint8_t)len; + + for (i = 0; i < len; i++) { + buf[i * 2 + 2] = cmd[i].addr; + buf[i * 2 + 3] = cmd[i].data; + } + + print_buf("NIOS II access request:\n", buf, 16); + + /* Send the command */ + status = usb->fn->bulk_transfer(usb->driver, PERIPHERAL_EP_OUT, + buf, sizeof(buf), + PERIPHERAL_TIMEOUT_MS); + if (status != 0) { + log_debug("Failed to submit NIOS II request: %s\n", + bladerf_strerror(status)); + return status; + } + + /* Read back the ACK. The command data is only used for a read operation, + * and is thrown away otherwise */ + status = usb->fn->bulk_transfer(usb->driver, PERIPHERAL_EP_IN, + buf, sizeof(buf), + PERIPHERAL_TIMEOUT_MS); + + if (dir == NIOS_PKT_LEGACY_MODE_DIR_READ && status == 0) { + for (i = 0; i < len; i++) { + cmd[i].data = buf[i * 2 + 3]; + } + } + + if (status == 0) { + print_buf("NIOS II access response:\n", buf, 16); + } else { + log_debug("Failed to receive NIOS II response: %s\n", + bladerf_strerror(status)); + } + + + return status; +} + +int nios_legacy_pio_read(struct bladerf *dev, uint8_t addr, uint32_t *data) +{ + int status; + size_t i; + struct uart_cmd cmd; + + *data = 0; + + for (i = 0; i < sizeof(*data); i++) { + assert((addr + i) <= UINT8_MAX); + cmd.addr = (uint8_t)(addr + i); + cmd.data = 0xff; + + status = nios_access(dev, NIOS_PKT_LEGACY_DEV_CONFIG, + USB_DIR_DEVICE_TO_HOST, &cmd, 1); + + if (status < 0) { + *data = 0; + return status; + } + + *data |= (cmd.data << (i * 8)); + } + + return 0; +} + +int nios_legacy_pio_write(struct bladerf *dev, uint8_t addr, uint32_t data) +{ + int status; + size_t i; + struct uart_cmd cmd; + + for (i = 0; i < sizeof(data); i++) { + assert((addr + i) <= UINT8_MAX); + cmd.addr = (uint8_t)(addr + i); + cmd.data = (data >> (i * 8)) & 0xff; + + status = nios_access(dev, NIOS_PKT_LEGACY_DEV_CONFIG, + USB_DIR_HOST_TO_DEVICE, &cmd, 1); + + if (status < 0) { + return status; + } + } + + return 0; +} + +int nios_legacy_config_read(struct bladerf *dev, uint32_t *val) +{ + int status; + + status = nios_legacy_pio_read(dev, NIOS_PKT_LEGACY_PIO_ADDR_CONTROL, val); + if (status == 0) { + log_verbose("%s: 0x%08x\n", __FUNCTION__, val); + } + + return status; +} + +int nios_legacy_config_write(struct bladerf *dev, uint32_t val) +{ + log_verbose("%s: Writing 0x%08x\n", __FUNCTION__, val); + return nios_legacy_pio_write(dev, NIOS_PKT_LEGACY_PIO_ADDR_CONTROL, val); +} + +int nios_legacy_get_fpga_version(struct bladerf *dev, + struct bladerf_version *ver) +{ + int i, status; + struct uart_cmd cmd; + + for (i = 0; i < 4; i++) { + cmd.addr = NIOS_PKT_LEGACY_DEV_FPGA_VERSION_ID + i; + cmd.data = 0xff; + + status = nios_access(dev, NIOS_PKT_LEGACY_DEV_CONFIG, + USB_DIR_DEVICE_TO_HOST, &cmd, 1); + + if (status != 0) { + log_debug("Failed to read FPGA version[%d]: %s\n", i, + bladerf_strerror(status)); + + return status; + } + + switch (i) { + case 0: + ver->major = cmd.data; + break; + case 1: + ver->minor = cmd.data; + break; + case 2: + ver->patch = cmd.data; + break; + case 3: + ver->patch |= (cmd.data << 8); + break; + default: + assert(!"Bug"); + } + } + + snprintf((char*)ver->describe, BLADERF_VERSION_STR_MAX, + "%d.%d.%d", ver->major, ver->minor, ver->patch); + + return status; +} + +int nios_legacy_get_timestamp(struct bladerf *dev, bladerf_direction dir, + uint64_t *value) +{ + int status = 0; + struct uart_cmd cmds[4]; + uint8_t timestamp_bytes[8]; + size_t i; + + /* Offset 16 is the time tamer according to the Nios firmware */ + cmds[0].addr = (dir == BLADERF_RX ? 16 : 24); + cmds[1].addr = (dir == BLADERF_RX ? 17 : 25); + cmds[2].addr = (dir == BLADERF_RX ? 18 : 26); + cmds[3].addr = (dir == BLADERF_RX ? 19 : 27); + cmds[0].data = cmds[1].data = cmds[2].data = cmds[3].data = 0xff; + + status = nios_access(dev, + NIOS_PKT_LEGACY_DEV_CONFIG, + USB_DIR_DEVICE_TO_HOST, + cmds, ARRAY_SIZE(cmds)); + if (status != 0) { + return status; + } else { + for (i = 0; i < 4; i++) { + timestamp_bytes[i] = cmds[i].data; + } + } + + cmds[0].addr = (dir == BLADERF_RX ? 20 : 28); + cmds[1].addr = (dir == BLADERF_RX ? 21 : 29); + cmds[2].addr = (dir == BLADERF_RX ? 22 : 30); + cmds[3].addr = (dir == BLADERF_RX ? 23 : 31); + cmds[0].data = cmds[1].data = cmds[2].data = cmds[3].data = 0xff; + + status = nios_access(dev, + NIOS_PKT_LEGACY_DEV_CONFIG, + USB_DIR_DEVICE_TO_HOST, + cmds, ARRAY_SIZE(cmds)); + + if (status) { + return status; + } else { + for (i = 0; i < 4; i++) { + timestamp_bytes[i + 4] = cmds[i].data; + } + } + + memcpy(value, timestamp_bytes, sizeof(*value)); + *value = LE64_TO_HOST(*value); + + return 0; +} + +int nios_legacy_si5338_read(struct bladerf *dev, uint8_t addr, uint8_t *data) +{ + int status; + struct uart_cmd cmd; + + cmd.addr = addr; + cmd.data = 0xff; + + status = nios_access(dev, NIOS_PKT_LEGACY_DEV_SI5338, + USB_DIR_DEVICE_TO_HOST, &cmd, 1); + + if (status == 0) { + *data = cmd.data; + log_verbose("%s: 0x%2.2x 0x%2.2x\n", __FUNCTION__, addr, *data); + } + + return status; +} + +int nios_legacy_si5338_write(struct bladerf *dev, uint8_t addr, uint8_t data) +{ + struct uart_cmd cmd; + + cmd.addr = addr; + cmd.data = data; + + log_verbose( "%s: 0x%2.2x 0x%2.2x\n", __FUNCTION__, addr, data ); + + return nios_access(dev, NIOS_PKT_LEGACY_DEV_SI5338, + USB_DIR_HOST_TO_DEVICE, &cmd, 1); +} + +int nios_legacy_lms6_read(struct bladerf *dev, uint8_t addr, uint8_t *data) +{ + int status; + struct uart_cmd cmd; + + cmd.addr = addr; + cmd.data = 0xff; + + status = nios_access(dev, NIOS_PKT_LEGACY_DEV_LMS, + USB_DIR_DEVICE_TO_HOST, &cmd, 1); + + if (status == 0) { + *data = cmd.data; + log_verbose( "%s: 0x%2.2x 0x%2.2x\n", __FUNCTION__, addr, *data ); + } + + return status; +} + +int nios_legacy_lms6_write(struct bladerf *dev, uint8_t addr, uint8_t data) +{ + int status; + struct uart_cmd cmd; + + cmd.addr = addr; + cmd.data = data; + + log_verbose( "%s: 0x%2.2x 0x%2.2x\n", __FUNCTION__, addr, data ); + + status = nios_access(dev, NIOS_PKT_LEGACY_DEV_LMS, + USB_DIR_HOST_TO_DEVICE, &cmd, 1); + + return status; +} + +int nios_legacy_ina219_read(struct bladerf *dev, uint8_t addr, uint16_t *data) +{ + log_debug("This operation is not supported by the legacy NIOS packet format\n"); + return BLADERF_ERR_UNSUPPORTED; +} + +int nios_legacy_ina219_write(struct bladerf *dev, uint8_t addr, uint16_t data) +{ + log_debug("This operation is not supported by the legacy NIOS packet format\n"); + return BLADERF_ERR_UNSUPPORTED; +} + +int nios_legacy_ad9361_spi_read(struct bladerf *dev, uint16_t cmd, + uint64_t *data) +{ + log_debug("This operation is not supported by the legacy NIOS packet format\n"); + return BLADERF_ERR_UNSUPPORTED; +} + +int nios_legacy_ad9361_spi_write(struct bladerf *dev, uint16_t cmd, + uint64_t data) +{ + log_debug("This operation is not supported by the legacy NIOS packet format\n"); + return BLADERF_ERR_UNSUPPORTED; +} + +int nios_legacy_adi_axi_read(struct bladerf *dev, uint32_t addr, + uint32_t *data) +{ + log_debug("This operation is not supported by the legacy NIOS packet format\n"); + return BLADERF_ERR_UNSUPPORTED; +} + +int nios_legacy_adi_axi_write(struct bladerf *dev, uint32_t cmd, + uint32_t data) +{ + log_debug("This operation is not supported by the legacy NIOS packet format\n"); + return BLADERF_ERR_UNSUPPORTED; +} + +int nios_legacy_rfic_command_read(struct bladerf *dev, + uint16_t cmd, + uint64_t *data) +{ + log_debug("This operation is not supported by the legacy NIOS packet format\n"); + return BLADERF_ERR_UNSUPPORTED; +} + +int nios_legacy_rfic_command_write(struct bladerf *dev, + uint16_t cmd, + uint64_t data) +{ + log_debug("This operation is not supported by the legacy NIOS packet format\n"); + return BLADERF_ERR_UNSUPPORTED; +} + +int nios_legacy_rffe_control_read(struct bladerf *dev, uint32_t *value) +{ + log_debug("This operation is not supported by the legacy NIOS packet format\n"); + return BLADERF_ERR_UNSUPPORTED; +} + +int nios_legacy_rffe_control_write(struct bladerf *dev, uint32_t value) +{ + log_debug("This operation is not supported by the legacy NIOS packet format\n"); + return BLADERF_ERR_UNSUPPORTED; +} + +int nios_legacy_rffe_fastlock_save(struct bladerf *dev, bool is_tx, + uint8_t rffe_profile, uint16_t nios_profile) +{ + log_debug("This operation is not supported by the legacy NIOS packet format\n"); + return BLADERF_ERR_UNSUPPORTED; +} + +int nios_legacy_ad56x1_vctcxo_trim_dac_read(struct bladerf *dev, uint16_t *value) +{ + log_debug("This operation is not supported by the legacy NIOS packet format\n"); + return BLADERF_ERR_UNSUPPORTED; +} + +int nios_legacy_ad56x1_vctcxo_trim_dac_write(struct bladerf *dev, uint16_t value) +{ + log_debug("This operation is not supported by the legacy NIOS packet format\n"); + return BLADERF_ERR_UNSUPPORTED; +} + +int nios_legacy_adf400x_read(struct bladerf *dev, uint8_t addr, uint32_t *data) +{ + log_debug("This operation is not supported by the legacy NIOS packet format\n"); + return BLADERF_ERR_UNSUPPORTED; +} + +int nios_legacy_adf400x_write(struct bladerf *dev, uint8_t addr, uint32_t data) +{ + log_debug("This operation is not supported by the legacy NIOS packet format\n"); + return BLADERF_ERR_UNSUPPORTED; +} + +int nios_legacy_vctcxo_trim_dac_write(struct bladerf *dev, uint8_t addr, uint16_t value) +{ + int status; + struct uart_cmd cmd; + int base; + + /* Only pass through writes of the DAC value, for compatibility with + * dac161s055 driver */ + if (addr != 0x08) { + return 0; + } + + /* FPGA v0.0.4 introduced a change to the location of the DAC registers */ + const bool legacy_location = + !have_cap(dev->board->get_capabilities(dev), BLADERF_CAP_UPDATED_DAC_ADDR); + + base = legacy_location ? 0 : 34; + + cmd.addr = base; + cmd.data = value & 0xff; + status = nios_access(dev, + legacy_location ? + NIOS_PKT_LEGACY_DEV_VCTCXO : + NIOS_PKT_LEGACY_DEV_CONFIG, + USB_DIR_HOST_TO_DEVICE, &cmd, 1); + + if (status < 0) { + return status; + } + + cmd.addr = base + 1; + cmd.data = (value >> 8) & 0xff; + status = nios_access(dev, + legacy_location ? + NIOS_PKT_LEGACY_DEV_VCTCXO : + NIOS_PKT_LEGACY_DEV_CONFIG, + USB_DIR_HOST_TO_DEVICE, &cmd, 1); + + return status; +} + +int nios_legacy_set_vctcxo_tamer_mode(struct bladerf *dev, + bladerf_vctcxo_tamer_mode mode) +{ + log_debug("This operation is not supported by the legacy NIOS packet format\n"); + return BLADERF_ERR_UNSUPPORTED; +} + +int nios_legacy_get_vctcxo_tamer_mode(struct bladerf *dev, + bladerf_vctcxo_tamer_mode *mode) +{ + log_debug("This operation is not supported by the legacy NIOS packet format\n"); + *mode = BLADERF_VCTCXO_TAMER_INVALID; + return BLADERF_ERR_UNSUPPORTED; +} + +static int get_iq_correction(struct bladerf *dev, uint8_t addr, int16_t *value) +{ + int i; + int status; + struct uart_cmd cmd; + + *value = 0; + for (i = status = 0; status == 0 && i < 2; i++) { + cmd.addr = i + addr; + cmd.data = 0xff; + status = nios_access(dev, NIOS_PKT_LEGACY_DEV_CONFIG, + USB_DIR_DEVICE_TO_HOST, &cmd, 1); + + *value |= (cmd.data << (i * 8)); + } + + return status; +} + +static int set_iq_correction(struct bladerf *dev, uint8_t addr, int16_t value) +{ + int i; + int status; + struct uart_cmd cmd; + + for (i = status = 0; status == 0 && i < 2; i++) { + cmd.addr = i + addr; + + cmd.data = (value >> (i * 8)) & 0xff; + status = nios_access(dev, NIOS_PKT_LEGACY_DEV_CONFIG, + USB_DIR_HOST_TO_DEVICE, &cmd, 1); + } + + return status; +} + + +int nios_legacy_get_iq_gain_correction(struct bladerf *dev, + bladerf_channel ch, int16_t *value) +{ + int status; + uint8_t addr; + + switch (ch) { + case BLADERF_CHANNEL_RX(0): + addr = NIOS_PKT_LEGACY_DEV_RX_GAIN_ADDR; + break; + + case BLADERF_CHANNEL_TX(0): + addr = NIOS_PKT_LEGACY_DEV_TX_GAIN_ADDR; + break; + + default: + log_debug("%s: invalid channel provided (0x%x)\n", + __FUNCTION__, ch); + + return BLADERF_ERR_INVAL; + } + + status = get_iq_correction(dev, addr, value); + + return status; +} + +int nios_legacy_get_iq_phase_correction(struct bladerf *dev, + bladerf_channel ch, int16_t *value) +{ + uint8_t addr; + + switch (ch) { + case BLADERF_CHANNEL_RX(0): + addr = NIOS_PKT_LEGACY_DEV_RX_PHASE_ADDR; + break; + + case BLADERF_CHANNEL_TX(0): + addr = NIOS_PKT_LEGACY_DEV_TX_PHASE_ADDR; + break; + + default: + log_debug("%s: invalid channel provided (0x%x)\n", + __FUNCTION__, ch); + + return BLADERF_ERR_INVAL; + } + + return get_iq_correction(dev, addr, value); +} + +int nios_legacy_set_iq_gain_correction(struct bladerf *dev, + bladerf_channel ch, int16_t value) +{ + uint8_t addr; + + switch (ch) { + case BLADERF_CHANNEL_RX(0): + addr = NIOS_PKT_LEGACY_DEV_RX_GAIN_ADDR; + log_verbose("Setting RX IQ Correction phase: %d\n", value); + break; + + case BLADERF_CHANNEL_TX(0): + addr = NIOS_PKT_LEGACY_DEV_TX_GAIN_ADDR; + log_verbose("Setting TX IQ Correction phase: %d\n", value); + break; + + default: + log_debug("%s: invalid channel provided (0x%x)\n", + __FUNCTION__, ch); + + return BLADERF_ERR_INVAL; + } + + return set_iq_correction(dev, addr, value); +} + +int nios_legacy_set_iq_phase_correction(struct bladerf *dev, + bladerf_channel ch, int16_t value) +{ + uint8_t addr; + + switch (ch) { + case BLADERF_CHANNEL_RX(0): + log_verbose("Setting RX IQ Correction phase: %d\n", value); + addr = NIOS_PKT_LEGACY_DEV_RX_PHASE_ADDR; + break; + + case BLADERF_CHANNEL_TX(0): + log_verbose("Setting TX IQ Correction phase: %d\n", value); + addr = NIOS_PKT_LEGACY_DEV_TX_PHASE_ADDR; + break; + + default: + log_debug("%s: invalid channel provided (0x%x)\n", + __FUNCTION__, ch); + + return BLADERF_ERR_INVAL; + } + + return set_iq_correction(dev, addr, value); +} + +int nios_legacy_xb200_synth_write(struct bladerf *dev, uint32_t value) +{ + log_verbose("%s: 0x%08x\n", __FUNCTION__, value); + return nios_legacy_pio_write(dev, + NIOS_PKT_LEGACY_PIO_ADDR_XB200_SYNTH, value); +} + +int nios_legacy_expansion_gpio_read(struct bladerf *dev, uint32_t *val) +{ + int status = nios_legacy_pio_read(dev, NIOS_PKT_LEGACY_PIO_ADDR_EXP, val); + + if (status == 0) { + log_verbose("%s: 0x%08x\n", __FUNCTION__, val); + } + + return status; +} + +int nios_legacy_expansion_gpio_write(struct bladerf *dev, + uint32_t mask, uint32_t val) +{ + int status; + uint32_t tmp; + + if (mask != 0xffffffff) { + status = nios_legacy_pio_read(dev, NIOS_PKT_LEGACY_PIO_ADDR_EXP, &tmp); + if (status != 0) { + return status; + } + + tmp &= ~mask; + tmp |= (val & mask); + val = tmp; + } + + log_verbose("%s: 0x%08x\n", __FUNCTION__, val); + return nios_legacy_pio_write(dev, NIOS_PKT_LEGACY_PIO_ADDR_EXP, val); +} + +int nios_legacy_expansion_gpio_dir_read(struct bladerf *dev, uint32_t *val) +{ + int status = nios_legacy_pio_read(dev, + NIOS_PKT_LEGACY_PIO_ADDR_EXP_DIR, val); + + if (status == 0) { + log_verbose("%s: 0x%08x\n", __FUNCTION__, val); + } + + return status; +} + +int nios_legacy_expansion_gpio_dir_write(struct bladerf *dev, + uint32_t mask, uint32_t val) +{ + int status; + uint32_t tmp; + + if (mask != 0xffffffff) { + status = nios_legacy_pio_read(dev, + NIOS_PKT_LEGACY_PIO_ADDR_EXP_DIR, &tmp); + + if (status != 0) { + return status; + } + + tmp &= ~mask; + tmp |= (val & mask); + val = tmp; + } + + log_verbose("%s: 0x%08\n", __FUNCTION__, val); + return nios_legacy_pio_write(dev, NIOS_PKT_LEGACY_PIO_ADDR_EXP_DIR, val); +} + +int nios_legacy_read_trigger(struct bladerf *dev, bladerf_channel ch, + bladerf_trigger_signal trigger, uint8_t *value) +{ + log_debug("This operation is not supported by the legacy NIOS packet format\n"); + return BLADERF_ERR_UNSUPPORTED; +} + +int nios_legacy_write_trigger(struct bladerf *dev, bladerf_channel ch, + bladerf_trigger_signal trigger, uint8_t value) +{ + log_debug("This operation is not supported by the legacy NIOS packet format\n"); + return BLADERF_ERR_UNSUPPORTED; +} diff --git a/Radio/HW/BladeRF/src/backend/usb/nios_legacy_access.h b/Radio/HW/BladeRF/src/backend/usb/nios_legacy_access.h new file mode 100644 index 0000000..92f4b77 --- /dev/null +++ b/Radio/HW/BladeRF/src/backend/usb/nios_legacy_access.h @@ -0,0 +1,488 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2014-2015 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 file defines functionality used to communicate with the NIOS II + * soft-core processor running on the FPGA versions prior to v0.2.x. + * (Although post v0.2.x FPGAs support this for reverse compatibility). + * + * The host communicates with the NIOS II via USB transfers to the Cypress FX3, + * which then communicates with the FPGA via a UART. This module packs and + * submits the requests, and receives and unpacks the responses. + * + * See the files in the top-level fpga_common/ directory for description + * and macros pertaining to the legacy packet format. + */ + +#ifndef BACKEND_USB_NIOS_LEGACY_ACCESS_H_ +#define BACKEND_USB_NIOS_LEGACY_ACCESS_H_ + +#include +#include +#include + +#include "board/board.h" +#include "usb.h" + +/** + * Perform a read from device/blocks connected via NIOS II PIO. + * + * @param dev Device handle + * @param[in] addr Virtual "register address." Use NIOS_LEGACY_PIO_ADDR_* + * definitions. + * @param[out] data Read data + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_pio_read(struct bladerf *dev, uint8_t addr, uint32_t *data); + +/** + * Perform a write to device/blocks connected via NIOS II PIO. + * + * @param dev Device handle + * @param[in] addr Virtual "register address." Use NIOS_LEGACY_PIO_ADDR_* + * definitions. + * @param[in] data Data to write + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_pio_write(struct bladerf *dev, uint8_t addr, uint32_t data); + +/** + * Read from the FPGA's config register + * + * @param dev Device handle + * @param[out] val On success, updated with config register value + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_config_read(struct bladerf *dev, uint32_t *val); + +/** + * Write to FPGA's config register + * + * @param dev Device handle + * @param[in] val Register value to write + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_config_write(struct bladerf *dev, uint32_t val); + +/** + * Read the FPGA version into the provided version structure + * + * @param dev Device handle + * @param[out] ver Updated with FPGA version (upon success) + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_get_fpga_version(struct bladerf *dev, + struct bladerf_version *ver); + +/** + * Read a timestamp counter value + * + * @param dev Device handle + * @param[in] dir Stream direction + * @param[out] timestamp On success, updated with the timestamp value + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_get_timestamp(struct bladerf *dev, + bladerf_direction dir, + uint64_t *timestamp); + +/** + * Read from an Si5338 register + * + * @param dev Device handle + * @param[in] addr Register address + * @param[out] data On success, updated with read data + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_si5338_read(struct bladerf *dev, uint8_t addr, uint8_t *data); + +/** + * Write to an Si5338 register + * + * @param dev Device handle + * @param[in] addr Register address + * @param[in] data Data to write + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_si5338_write(struct bladerf *dev, uint8_t addr, uint8_t data); + +/** + * Read from an LMS6002D register + * + * @param dev Device handle + * @param[in] addr Register address + * @param[out] data On success, updated with data read from the device + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_lms6_read(struct bladerf *dev, uint8_t addr, uint8_t *data); + +/** + * Write to an LMS6002D register + * + * @param dev Device handle + * @param[in] addr Register address + * @param[out] data Register data + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_lms6_write(struct bladerf *dev, uint8_t addr, uint8_t data); + +/** + * Read from an INA219 register + * + * @param dev Device handle + * @param[in] addr Register address + * @param[out] data Data + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_ina219_read(struct bladerf *dev, uint8_t addr, uint16_t *data); + +/** + * Write to an INA219 register + * + * @param dev Device handle + * @param[in] addr Register address + * @param[in] data Data + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_ina219_write(struct bladerf *dev, uint8_t addr, uint16_t data); + +/** + * Read the AD9361 over SPI. + * + * @param dev Device handle + * @param[in] cmd Command + * @param[out] data Data + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_ad9361_spi_read(struct bladerf *dev, + uint16_t cmd, + uint64_t *data); + +/** + * Write to the AD9361 over SPI. + * + * @param dev Device handle + * @param[in] cmd Command + * @param[in] data Data + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_ad9361_spi_write(struct bladerf *dev, + uint16_t cmd, + uint64_t data); + +/** + * Read the ADI AXI memory mapped region. + * + * @param dev Device handle + * @param[in] addr Address + * @param[out] data Data + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_adi_axi_read(struct bladerf *dev, + uint32_t addr, + uint32_t *data); + +/** + * Write to the ADI AXI memory mapped region. + * + * @param dev Device handle + * @param[in] addr Address + * @param[in] data Data + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_adi_axi_write(struct bladerf *dev, + uint32_t addr, + uint32_t data); + +/** + * Read RFIC command + * + * @param dev Device handle + * @param[in] cmd Command + * @param[out] data Data + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_rfic_command_read(struct bladerf *dev, + uint16_t cmd, + uint64_t *data); + +/** + * Write RFIC command + * + * @param dev Device handle + * @param[in] cmd Command + * @param[in] data Data + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_rfic_command_write(struct bladerf *dev, + uint16_t cmd, + uint64_t data); + +/** + * Read RFFE control register. + * + * @param dev Device handle + * @param[in] len Data buffer length + * @param[out] value Value + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_rffe_control_read(struct bladerf *dev, uint32_t *value); + +/** + * Write RFFE control register. + * + * @param dev Device handle + * @param[in] len Data buffer length + * @param[in] value Value + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_rffe_control_write(struct bladerf *dev, uint32_t value); + +/** + * Save an RFFE fast lock profile to the Nios. + * + * @param dev Device handle + * @param[in] is_tx True if TX profile, false if RX profile + * @param[in] rffe_profile RFFE profile to save + * @param[in] nios_profile Where to save the profile in the Nios + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_rffe_fastlock_save(struct bladerf *dev, bool is_tx, + uint8_t rffe_profile, + uint16_t nios_profile); + +/** + * Write to the AD56X1 VCTCXO trim DAC. + * + * @param dev Device handle + * @param[in] value Value + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_ad56x1_vctcxo_trim_dac_write(struct bladerf *dev, + uint16_t value); + +/** + * Read the AD56X1 VCTCXO trim DAC. + * + * @param dev Device handle + * @param[out] value Data + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_ad56x1_vctcxo_trim_dac_read(struct bladerf *dev, + uint16_t *value); + +/** + * Write to the ADF400X. + * + * @param dev Device handle + * @param[in] addr Address + * @param[in] data Data + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_adf400x_write(struct bladerf *dev, uint8_t addr, uint32_t data); + +/** + * Read from the ADF400X. + * + * @param dev Device handle + * @param[in] addr Address + * @param[out] data Data + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_adf400x_read(struct bladerf *dev, uint8_t addr, uint32_t *data); + +/** + * Write to the VCTCXO trim DAC. + * + * @param dev Device handle + * @param[in] addr Register (should be 0x08) + * @param[in] value Value + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_vctcxo_trim_dac_write(struct bladerf *dev, + uint8_t addr, + uint16_t value); + +/** + * Read a IQ gain correction value + * + * @param dev Device handle + * @param[in] ch Channel + * @param[out] value On success, updated with phase correction value + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_get_iq_gain_correction(struct bladerf *dev, + bladerf_channel ch, + int16_t *value); + +/** + * Read a IQ phase correction value + * + * @param dev Device handle + * @param[in] ch Channel + * @param[out] value On success, updated with phase correction value + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_get_iq_phase_correction(struct bladerf *dev, + bladerf_channel ch, + int16_t *value); + +/** + * Write an IQ gain correction value + * + * @param dev Device handle + * @param[in] ch Channel + * @param[in] value Correction value to write + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_set_iq_gain_correction(struct bladerf *dev, + bladerf_channel ch, + int16_t value); + +/** + * Write an IQ phase correction value + * + * @param dev Device handle + * @param[in] ch Channel + * @param[in] value Correction value to write + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_set_iq_phase_correction(struct bladerf *dev, + bladerf_channel ch, + int16_t value); + +/** + * Write a value to the XB-200's ADF4351 synthesizer + * + * @param dev Device handle + * @param[in] value Write value + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_xb200_synth_write(struct bladerf *dev, uint32_t value); + +/** + * Read from expanion GPIO + * + * @param dev Device handle + * @param[out] value On success, updated with read value + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_expansion_gpio_read(struct bladerf *dev, uint32_t *val); + +/** + * Write to expansion GPIO + * + * @param dev Device handle + * @param[in[ mask Mask denoting bits to write + * @param[in] value Value to write + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_expansion_gpio_write(struct bladerf *dev, + uint32_t mask, + uint32_t val); + +/** + * Read from expansion GPIO direction register + * + * @param dev Device handle + * @param[out] value On success, updated with read value + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_expansion_gpio_dir_read(struct bladerf *dev, uint32_t *val); + +/** + * Write to expansion GPIO direction register + * + * @param dev Device handle + * @param[in] mask Mask denoting bits to configure + * @param[in] output '1' bit denotes an output, '0' bit denotes an input + * + * @return 0 on success, BLADERF_ERR_* code on error. + */ +int nios_legacy_expansion_gpio_dir_write(struct bladerf *dev, + uint32_t mask, + uint32_t outputs); + +/** + * Read trigger register value + * + * @param dev Device handle + * @param[in] ch Channel + * @param[in] trigger Trigger to read from + * @param[out] value On success, updated with register value + * + * + * @return 0 on success, BLADERF_ERR_* code on error + */ +int nios_legacy_read_trigger(struct bladerf *dev, + bladerf_channel ch, + bladerf_trigger_signal trigger, + uint8_t *value); + +/** + * Write trigger register value + * + * @param dev Device handle + * @param[in] ch Channel + * @param[in] trigger Trigger to read + * @param[in] value Value to write + * + * @return 0 on success, BLADERF_ERR_* code on error + */ +int nios_legacy_write_trigger(struct bladerf *dev, + bladerf_channel ch, + bladerf_trigger_signal trigger, + uint8_t value); + +#endif diff --git a/Radio/HW/BladeRF/src/backend/usb/usb.c b/Radio/HW/BladeRF/src/backend/usb/usb.c new file mode 100644 index 0000000..fba2c33 --- /dev/null +++ b/Radio/HW/BladeRF/src/backend/usb/usb.c @@ -0,0 +1,1430 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2014-2017 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 +#include +#include +#include +#include + +#include "rel_assert.h" +#include "log.h" +#include "minmax.h" +#include "conversions.h" +#include "usb.h" + +#include "board/board.h" +#include "backend/backend.h" +#include "backend/backend_config.h" +#include "backend/usb/usb.h" +#include "driver/fx3_fw.h" +#include "streaming/async.h" +#include "helpers/version.h" + +#include "bladeRF.h" +#include "nios_pkt_formats.h" +#include "nios_legacy_access.h" +#include "nios_access.h" + +#if ENABLE_USB_DEV_RESET_ON_OPEN +bool bladerf_usb_reset_device_on_open = true; +#endif + +static const struct usb_driver *usb_driver_list[] = BLADERF_USB_BACKEND_LIST; + +/* FW declaration of fn table declared at the end of this file */ +const struct backend_fns backend_fns_usb_legacy; + +/* Vendor command wrapper to gets a 32-bit integer and supplies a wIndex */ +static inline int vendor_cmd_int_windex(struct bladerf *dev, uint8_t cmd, + uint16_t windex, int32_t *val) +{ + struct bladerf_usb *usb = dev->backend_data; + + return usb->fn->control_transfer(usb->driver, + USB_TARGET_DEVICE, + USB_REQUEST_VENDOR, + USB_DIR_DEVICE_TO_HOST, + cmd, 0, windex, + val, sizeof(uint32_t), + CTRL_TIMEOUT_MS); +} + +/* Vendor command wrapper to get a 32-bit integer and supplies wValue */ +static inline int vendor_cmd_int_wvalue(struct bladerf *dev, uint8_t cmd, + uint16_t wvalue, int32_t *val) +{ + struct bladerf_usb *usb = dev->backend_data; + + return usb->fn->control_transfer(usb->driver, + USB_TARGET_DEVICE, + USB_REQUEST_VENDOR, + USB_DIR_DEVICE_TO_HOST, + cmd, wvalue, 0, + val, sizeof(uint32_t), + CTRL_TIMEOUT_MS); +} + + +/* Vendor command that gets/sets a 32-bit integer value */ +static inline int vendor_cmd_int(struct bladerf *dev, uint8_t cmd, + usb_direction dir, int32_t *val) +{ + struct bladerf_usb *usb = dev->backend_data; + + return usb->fn->control_transfer(usb->driver, + USB_TARGET_DEVICE, + USB_REQUEST_VENDOR, + dir, cmd, 0, 0, + val, sizeof(int32_t), + CTRL_TIMEOUT_MS); +} + +static inline int change_setting(struct bladerf *dev, uint8_t setting) +{ + int status; + struct bladerf_usb *usb = dev->backend_data; + + log_verbose("Changing to USB alt setting %u\n", setting); + + status = usb->fn->change_setting(usb->driver, setting); + if (status != 0) { + log_debug("Failed to change setting: %s\n", bladerf_strerror(status)); + } + + return status; +} + +static int usb_is_fpga_configured(struct bladerf *dev) +{ + int result = -1; + int status; + + /* This environment variable provides a means to force libbladeRF to not + * attempt to access the FPGA. + * + * This provides a workaround for the situation where a user did not remove + * an FPGA in SPI flash prior to flashing new firmware and updating + * libbladeRF. Specifically, this has proven to be a problem with pre-v0.0.1 + * FPGA images, as they do not provide version readback functionality. + */ + if (getenv("BLADERF_FORCE_NO_FPGA_PRESENT")) { + log_debug("Reporting no FPGA present - " + "BLADERF_FORCE_NO_FPGA_PRESENT is set.\n"); + return 0; + } + + status = vendor_cmd_int(dev, BLADE_USB_CMD_QUERY_FPGA_STATUS, + USB_DIR_DEVICE_TO_HOST, &result); + + if (status < 0) { + return status; + } else if (result == 0 || result == 1) { + return result; + } else { + log_debug("Unexpected result from FPGA status query: %d\n", result); + return BLADERF_ERR_UNEXPECTED; + } +} + +static int usb_set_fpga_protocol(struct bladerf *dev, backend_fpga_protocol fpga_protocol) +{ + if (fpga_protocol == BACKEND_FPGA_PROTOCOL_NIOSII_LEGACY) { + dev->backend = &backend_fns_usb_legacy; + } else if (fpga_protocol == BACKEND_FPGA_PROTOCOL_NIOSII) { + dev->backend = &backend_fns_usb; + } else { + log_error("Unknown FPGA protocol: %d\n", fpga_protocol); + return BLADERF_ERR_INVAL; + } + + return 0; +} + +static bladerf_fpga_source usb_get_fpga_source(struct bladerf *dev) +{ + int result = -1; + int status; + + status = vendor_cmd_int(dev, BLADE_USB_CMD_QUERY_FPGA_SOURCE, + USB_DIR_DEVICE_TO_HOST, &result); + + if (status < 0) { + log_debug("%s: vendor_cmd_int returned %s\n", __FUNCTION__, + bladerf_strerror(status)); + return BLADERF_FPGA_SOURCE_UNKNOWN; + } else if (0 == result || 1 == result || 2 == result) { + return (bladerf_fpga_source)result; + } else { + log_debug("Unexpected result from FPGA source query: %d\n", result); + return BLADERF_FPGA_SOURCE_UNKNOWN; + } +} + +/* After performing a flash operation, switch back to either RF_LINK or the + * FPGA loader. + */ +static int restore_post_flash_setting(struct bladerf *dev) +{ + int fpga_loaded = usb_is_fpga_configured(dev); + int status; + + if (fpga_loaded < 0) { + status = fpga_loaded; + log_debug("Failed to determine if FPGA is loaded (%d)\n", fpga_loaded); + } else if (fpga_loaded) { + status = change_setting(dev, USB_IF_RF_LINK); + } else { + status = change_setting(dev, USB_IF_CONFIG); + } + + if (status < 0) { + log_debug("Failed to restore alt setting: %s\n", + bladerf_strerror(status)); + } + return status; +} + +static bool usb_matches(bladerf_backend backend) +{ + return backend == BLADERF_BACKEND_ANY || + backend == BLADERF_BACKEND_LINUX || + backend == BLADERF_BACKEND_LIBUSB || + backend == BLADERF_BACKEND_CYPRESS; +} + +static int usb_probe(backend_probe_target probe_target, + struct bladerf_devinfo_list *info_list) +{ + int status; + size_t i; + + for (i = status = 0; i < ARRAY_SIZE(usb_driver_list); i++) { + status = usb_driver_list[i]->fn->probe(probe_target, info_list); + } + + return status; +} + +static void usb_close(struct bladerf *dev) +{ + int status; + struct bladerf_usb *usb = dev->backend_data; + + if (usb != NULL) { + /* It seems we need to switch back to our NULL interface before closing, + * or else our device doesn't close upon exit in OSX and then fails to + * re-open cleanly */ + status = usb->fn->change_setting(usb->driver, USB_IF_NULL); + if (status != 0) { + log_error("Failed to switch to NULL interface: %s\n", + bladerf_strerror(status)); + } + + usb->fn->close(usb->driver); + free(usb); + dev->backend_data = NULL; + } +} + +static int usb_is_fw_ready(struct bladerf *dev) +{ + int status; + int result; + + status = vendor_cmd_int(dev, BLADE_USB_CMD_QUERY_DEVICE_READY, + USB_DIR_DEVICE_TO_HOST, &result); + if (status < 0) { + return status; + } else if (result == 0 || result == 1) { + return result; + } else { + log_debug("Unexpected result from firmware status query: %d\n", result); + return BLADERF_ERR_UNEXPECTED; + } +} + +static int usb_get_handle(struct bladerf *dev, + void **handle) +{ + struct bladerf_usb *usb = dev->backend_data; + int status; + + status = usb->fn->get_handle(usb->driver, handle); + + return status; +} + +static int usb_get_fw_version(struct bladerf *dev, + struct bladerf_version *version) +{ + struct bladerf_usb *usb = dev->backend_data; + int status; + + status = usb->fn->get_string_descriptor(usb->driver, + BLADE_USB_STR_INDEX_FW_VER, + (unsigned char *)version->describe, + BLADERF_VERSION_STR_MAX); + if (status == 0) { + status = str2version(version->describe, version); + } else { + log_warning("Failed to retrieve firmware version. This may be due " + "to an old firmware version that does not support " + "this request. A firmware update via the bootloader is " + "required.\n\n"); + status = BLADERF_ERR_UPDATE_FW; + } + + return status; +} + +static int usb_get_fpga_version(struct bladerf *dev, + struct bladerf_version *version) +{ + int status; + + status = change_setting(dev, USB_IF_RF_LINK); + if (status < 0) { + return status; + } + + /* Read and store FPGA version info. This is only possible after + * we've entered RF link mode. + * + * The legacy mode is used here since we can't yet determine if + * the FPGA is capable of using the newer packet formats. */ + return nios_legacy_get_fpga_version(dev, version); +} + +static int usb_open(struct bladerf *dev, struct bladerf_devinfo *info) +{ + int status; + size_t i; + struct bladerf_usb *usb; + + usb = malloc(sizeof(*usb)); + if (usb == NULL) { + return BLADERF_ERR_MEM; + } + + /* Try each matching usb driver */ + for (i = 0; i < ARRAY_SIZE(usb_driver_list); i++) { + if (info->backend == BLADERF_BACKEND_ANY + || usb_driver_list[i]->id == info->backend) { + usb->fn = usb_driver_list[i]->fn; + status = usb->fn->open(&usb->driver, info, &dev->ident); + if (status == 0) { + break; + } else if (status == BLADERF_ERR_NODEV) { + continue; + } else { + free(usb); + return status; + } + } + } + + /* If no usb driver was found */ + if (i == ARRAY_SIZE(usb_driver_list)) { + free(usb); + return BLADERF_ERR_NODEV; + } + + /* Default to legacy-mode access until we determine the FPGA is + * capable of handling newer request formats */ + dev->backend = &backend_fns_usb_legacy; + dev->backend_data = usb; + + /* Just out of paranoia, put the device into a known state */ + status = change_setting(dev, USB_IF_NULL); + if (status < 0) { + log_debug("Failed to switch to USB_IF_NULL\n"); + goto error; + } + +error: + if (status != 0) { + usb_close(dev); + } + + return status; +} + +static int usb_get_vid_pid(struct bladerf *dev, uint16_t *vid, uint16_t *pid) +{ + struct bladerf_usb *usb = dev->backend_data; + + return usb->fn->get_vid_pid(usb->driver, vid, pid); +} + +static int usb_get_flash_id(struct bladerf *dev, uint8_t *mid, uint8_t *did) +{ + int status; + int result; + + status = vendor_cmd_int(dev, BLADE_USB_CMD_QUERY_FLASH_ID, + USB_DIR_DEVICE_TO_HOST, &result); + if (status < 0) { + log_debug("Could not read flash manufacturer ID and/or device ID. %s.\n", + bladerf_strerror(status)); + } else { + *did = result & 0xFF; + *mid = (result >> 8) & 0xFF; + } + return status; +} + +static int begin_fpga_programming(struct bladerf *dev) +{ + int result; + int status = vendor_cmd_int(dev, BLADE_USB_CMD_BEGIN_PROG, + USB_DIR_DEVICE_TO_HOST, &result); + + if (status != 0) { + return status; + } else if (result != 0) { + log_debug("Startg fpga programming, result = %d\n", result); + return BLADERF_ERR_UNEXPECTED; + } else { + return 0; + } +} + +static int usb_load_fpga(struct bladerf *dev, const uint8_t *image, size_t image_size) +{ + struct bladerf_usb *usb = dev->backend_data; + + unsigned int wait_count; + const unsigned int timeout_ms = (3 * CTRL_TIMEOUT_MS); + int status; + + /* Switch to the FPGA configuration interface */ + status = change_setting(dev, USB_IF_CONFIG); + if(status < 0) { + log_debug("Failed to switch to FPGA config setting: %s\n", + bladerf_strerror(status)); + return status; + } + + /* Begin programming */ + status = begin_fpga_programming(dev); + if (status < 0) { + log_debug("Failed to initiate FPGA programming: %s\n", + bladerf_strerror(status)); + return status; + } + + /* Send the file down */ + assert(image_size <= UINT32_MAX); + status = usb->fn->bulk_transfer(usb->driver, PERIPHERAL_EP_OUT, + (void *)image, + (uint32_t)image_size, + timeout_ms); + if (status < 0) { + log_debug("Failed to write FPGA bitstream to FPGA: %s\n", + bladerf_strerror(status)); + return status; + } + + /* Poll FPGA status to determine if programming was a success */ + wait_count = 10; + status = 0; + + while (wait_count > 0 && status == 0) { + status = usb_is_fpga_configured(dev); + if (status == 1) { + break; + } + + usleep(200000); + wait_count--; + } + + /* Failed to determine if FPGA is loaded */ + if (status < 0) { + log_debug("Failed to determine if FPGA is loaded: %s\n", + bladerf_strerror(status)); + return status; + } else if (wait_count == 0 && status != 0) { + log_debug("Timeout while waiting for FPGA configuration status\n"); + return BLADERF_ERR_TIMEOUT; + } + + return 0; +} + +static inline int perform_erase(struct bladerf *dev, uint16_t block) +{ + int status, erase_ret; + struct bladerf_usb *usb = dev->backend_data; + + status = usb->fn->control_transfer(usb->driver, + USB_TARGET_DEVICE, + USB_REQUEST_VENDOR, + USB_DIR_DEVICE_TO_HOST, + BLADE_USB_CMD_FLASH_ERASE, + 0, block, + &erase_ret, sizeof(erase_ret), + CTRL_TIMEOUT_MS); + + + return status; +} + +static int usb_erase_flash_blocks(struct bladerf *dev, + uint32_t eb, + uint16_t count) +{ + int status, restore_status; + uint16_t i; + + status = change_setting(dev, USB_IF_SPI_FLASH); + if (status != 0) { + return status; + } + + log_info("Erasing %u block%s starting at block %u\n", count, + 1 == count ? "" : "s", eb); + + for (i = 0; i < count; i++) { + log_info("Erasing block %u (%u%%)...%c", eb + i, + (i + 1) == count ? 100 : 100 * i / count, + (i + 1) == count ? '\n' : '\r'); + + status = perform_erase(dev, eb + i); + if (status != 0) { + log_debug("Failed to erase block %u: %s\n", eb + i, + bladerf_strerror(status)); + goto error; + } + } + + log_info("Done erasing %u block%s\n", count, 1 == count ? "" : "s"); + +error: + restore_status = restore_post_flash_setting(dev); + return status != 0 ? status : restore_status; +} + +static inline int read_page(struct bladerf *dev, uint8_t read_operation, + uint16_t page, uint8_t *buf) +{ + struct bladerf_usb *usb = dev->backend_data; + bladerf_dev_speed usb_speed; + int status; + int32_t op_status; + uint16_t read_size; + uint16_t offset; + uint8_t request; + + if (usb->fn->get_speed(usb->driver, &usb_speed) != 0) { + log_debug("Error getting USB speed in %s\n", __FUNCTION__); + return BLADERF_ERR_UNEXPECTED; + } + + if (usb_speed == BLADERF_DEVICE_SPEED_SUPER) { + read_size = dev->flash_arch->psize_bytes; + } else if (usb_speed == BLADERF_DEVICE_SPEED_HIGH) { + read_size = 64; + } else { + log_debug("Encountered unknown USB speed in %s\n", __FUNCTION__); + return BLADERF_ERR_UNEXPECTED; + } + + if (read_operation == BLADE_USB_CMD_FLASH_READ || + read_operation == BLADE_USB_CMD_READ_OTP) { + + status = vendor_cmd_int_windex(dev, read_operation, page, &op_status); + if (status != 0) { + return status; + } else if (op_status != 0) { + log_error("Firmware page read (op=%d) failed at page %u: %d\n", + read_operation, page, op_status); + return BLADERF_ERR_UNEXPECTED; + } + + /* Both of these operations require a read from the FW's page buffer */ + request = BLADE_USB_CMD_READ_PAGE_BUFFER; + + } else if (read_operation == BLADE_USB_CMD_READ_CAL_CACHE) { + request = read_operation; + } else { + assert(!"Bug - invalid read_operation value"); + return BLADERF_ERR_UNEXPECTED; + } + + /* Retrieve data from the firmware page buffer */ + for (offset = 0; offset < dev->flash_arch->psize_bytes; offset += read_size) { + status = usb->fn->control_transfer(usb->driver, + USB_TARGET_DEVICE, + USB_REQUEST_VENDOR, + USB_DIR_DEVICE_TO_HOST, + request, + 0, + offset, /* in bytes */ + buf + offset, + read_size, + CTRL_TIMEOUT_MS); + + if(status < 0) { + log_debug("Failed to read page buffer at offset 0x%02x: %s\n", + offset, bladerf_strerror(status)); + return status; + } + } + + return 0; +} + +static int usb_read_flash_pages(struct bladerf *dev, + uint8_t *buf, + uint32_t page_u32, + uint32_t count_u32) +{ + int status; + size_t n_read; + uint16_t i; + + /* 16-bit control transfer fields are used for these. + * The current bladeRF build only has a 4MiB flash, anyway. */ + const uint16_t page = (uint16_t)page_u32; + const uint16_t count = (uint16_t)count_u32; + + assert(page == page_u32); + assert(count == count_u32); + + status = change_setting(dev, USB_IF_SPI_FLASH); + if (status != 0) { + return status; + } + + log_info("Reading %u page%s starting at page %u\n", count, + 1 == count ? "" : "s", page); + + for (n_read = i = 0; i < count; i++) { + log_info("Reading page %u (%u%%)...%c", page + i, + (i + 1) == count ? 100 : 100 * i / count, + (i + 1) == count ? '\n' : '\r'); + + status = + read_page(dev, BLADE_USB_CMD_FLASH_READ, page + i, buf + n_read); + if (status != 0) { + goto error; + } + + n_read += dev->flash_arch->psize_bytes; + } + + log_info("Done reading %u page%s\n", count, 1 == count ? "" : "s"); + +error: + status = restore_post_flash_setting(dev); + return status; +} + +static int write_page(struct bladerf *dev, uint8_t write_operation, + uint16_t page, const uint8_t *buf) +{ + int status; + int32_t commit_status; + uint16_t offset; + uint16_t write_size; + struct bladerf_usb *usb = dev->backend_data; + bladerf_dev_speed usb_speed; + + if (usb->fn->get_speed(usb->driver, &usb_speed) != 0) { + log_debug("Error getting USB speed in %s\n", __FUNCTION__); + return BLADERF_ERR_UNEXPECTED; + } + + if (usb_speed == BLADERF_DEVICE_SPEED_SUPER) { + write_size = dev->flash_arch->psize_bytes; + } else if (usb_speed == BLADERF_DEVICE_SPEED_HIGH) { + write_size = 64; + } else { + assert(!"BUG - unexpected device speed"); + return BLADERF_ERR_UNEXPECTED; + } + + /* Write the data to the firmware's page buffer. + * Casting away the buffer's const-ness here is gross, but this buffer + * will not be written to on an out transfer. */ + for (offset = 0; offset < dev->flash_arch->psize_bytes; offset += write_size) { + status = usb->fn->control_transfer(usb->driver, + USB_TARGET_DEVICE, + USB_REQUEST_VENDOR, + USB_DIR_HOST_TO_DEVICE, + BLADE_USB_CMD_WRITE_PAGE_BUFFER, + 0, + offset, + (uint8_t*)&buf[offset], + write_size, + CTRL_TIMEOUT_MS); + + if(status < 0) { + log_error("Failed to write page buffer at offset 0x%02x " + "for page %u: %s\n", + offset, page, bladerf_strerror(status)); + return status; + } + } + + /* Commit the page to flash */ + status = vendor_cmd_int_windex(dev, write_operation, page, &commit_status); + + if (status != 0) { + log_error("Failed to commit page %u: %s\n", page, + bladerf_strerror(status)); + return status; + + } else if (commit_status != 0) { + log_error("Failed to commit page %u, FW returned %d\n", page, + commit_status); + + return BLADERF_ERR_UNEXPECTED; + } + + return 0; +} + +static int usb_write_flash_pages(struct bladerf *dev, + const uint8_t *buf, + uint32_t page_u32, + uint32_t count_u32) + +{ + int status, restore_status; + uint16_t i; + size_t n_written; + + /* 16-bit control transfer fields are used for these. + * The current bladeRF build only has a 4MiB flash, anyway. */ + const uint16_t page = (uint16_t)page_u32; + const uint16_t count = (uint16_t)count_u32; + + assert(page == page_u32); + assert(count == count_u32); + + status = change_setting(dev, USB_IF_SPI_FLASH); + if (status != 0) { + return status; + } + + log_info("Writing %u page%s starting at page %u\n", count, + 1 == count ? "" : "s", page); + + n_written = 0; + for (i = 0; i < count; i++) { + log_info("Writing page %u (%u%%)...%c", page + i, + (i + 1) == count ? 100 : 100 * i / count, + (i + 1) == count ? '\n' : '\r'); + + status = write_page(dev, BLADE_USB_CMD_FLASH_WRITE, page + i, buf + n_written); + if (status) { + goto error; + } + + n_written += dev->flash_arch->psize_bytes; + } + log_info("Done writing %u page%s\n", count, 1 == count ? "" : "s"); + +error: + restore_status = restore_post_flash_setting(dev); + if (status != 0) { + return status; + } else if (restore_status != 0) { + return restore_status; + } else { + return 0; + } +} + +static int usb_device_reset(struct bladerf *dev) +{ + struct bladerf_usb *usb = dev->backend_data; + + return usb->fn->control_transfer(usb->driver, USB_TARGET_DEVICE, + USB_REQUEST_VENDOR, + USB_DIR_HOST_TO_DEVICE, + BLADE_USB_CMD_RESET, + 0, 0, 0, 0, CTRL_TIMEOUT_MS); + +} + +static int usb_jump_to_bootloader(struct bladerf *dev) +{ + struct bladerf_usb *usb = dev->backend_data; + + return usb->fn->control_transfer(usb->driver, USB_TARGET_DEVICE, + USB_REQUEST_VENDOR, + USB_DIR_HOST_TO_DEVICE, + BLADE_USB_CMD_JUMP_TO_BOOTLOADER, + 0, 0, 0, 0, CTRL_TIMEOUT_MS); +} + +static int usb_get_cal(struct bladerf *dev, char *cal) +{ + const uint16_t dummy_page = 0; + int status, restore_status; + + assert(CAL_BUFFER_SIZE == dev->flash_arch->psize_bytes); + + status = change_setting(dev, USB_IF_SPI_FLASH); + if (status) { + return status; + } + + status = read_page(dev, BLADE_USB_CMD_READ_CAL_CACHE, + dummy_page, (uint8_t*)cal); + + restore_status = restore_post_flash_setting(dev); + return status == 0 ? restore_status : status; +} + +static int usb_get_otp(struct bladerf *dev, char *otp) +{ + int status, restore_status; + const uint16_t dummy_page = 0; + + status = change_setting(dev, USB_IF_SPI_FLASH); + if (status) { + return status; + } + + status = read_page(dev, BLADE_USB_CMD_READ_OTP, dummy_page, (uint8_t*)otp); + restore_status = restore_post_flash_setting(dev); + return status == 0 ? restore_status : status; +} + +static int usb_write_otp(struct bladerf *dev, char *otp) +{ + int status, restore_status; + const uint16_t dummy_page = 0; + + status = change_setting(dev, USB_IF_SPI_FLASH); + if (status) { + return status; + } + + status = write_page(dev, BLADE_USB_CMD_WRITE_OTP, dummy_page, (uint8_t*)otp); + restore_status = restore_post_flash_setting(dev); + return status == 0 ? restore_status : status; +} + +static int usb_lock_otp(struct bladerf *dev) +{ + int status, restore_status, commit_status; + + status = change_setting(dev, USB_IF_SPI_FLASH); + if (status) { + return status; + } + + status = vendor_cmd_int_windex(dev, BLADE_USB_CMD_LOCK_OTP, + 0, &commit_status); + + if (commit_status != 0) { + log_error("Failed to lock OTP, FW returned %d\n", commit_status); + if (status == 0) + status = commit_status; + } + + restore_status = restore_post_flash_setting(dev); + return status == 0 ? restore_status : status; +} + +static int usb_get_device_speed(struct bladerf *dev, bladerf_dev_speed *speed) +{ + struct bladerf_usb *usb = dev->backend_data; + + return usb->fn->get_speed(usb->driver, speed); +} + +static int usb_set_firmware_loopback(struct bladerf *dev, bool enable) { + int result; + int status; + + status = vendor_cmd_int_wvalue(dev, BLADE_USB_CMD_SET_LOOPBACK, + enable, &result); + if (status != 0) { + return status; + } + + + status = change_setting(dev, USB_IF_NULL); + if (status == 0) { + status = change_setting(dev, USB_IF_RF_LINK); + } + + return status; +} + +static int usb_get_firmware_loopback(struct bladerf *dev, bool *is_enabled) +{ + int status, result; + + status = vendor_cmd_int(dev, BLADE_USB_CMD_GET_LOOPBACK, + USB_DIR_DEVICE_TO_HOST, &result); + + if (status == 0) { + *is_enabled = (result != 0); + } + + return status; +} + +static int usb_enable_module(struct bladerf *dev, bladerf_direction dir, bool enable) +{ + int status; + int32_t fx3_ret = -1; + const uint16_t val = enable ? 1 : 0; + const uint8_t cmd = (dir == BLADERF_RX) ? + BLADE_USB_CMD_RF_RX : BLADE_USB_CMD_RF_TX; + + status = vendor_cmd_int_wvalue(dev, cmd, val, &fx3_ret); + if (status != 0) { + log_debug("Could not enable RF %s (%d): %s\n", + (dir == BLADERF_RX) ? "RX" : "TX", + status, bladerf_strerror(status)); + + } else if (fx3_ret != 0) { + log_warning("FX3 reported error=0x%x when %s RF %s\n", + fx3_ret, + enable ? "enabling" : "disabling", + (dir == BLADERF_RX) ? "RX" : "TX"); + + /* FIXME: Work around what seems to be a harmless failure. + * It appears that in firmware or in the lib, we may be + * attempting to disable an already disabled channel, or + * enabling an already enabled channel. + * + * Further investigation required + * + * 0x44 corresponds to CY_U3P_ERROR_ALREADY_STARTED + */ + if (fx3_ret != 0x44) { + status = BLADERF_ERR_UNEXPECTED; + } + } + + return status; +} + +static int usb_init_stream(struct bladerf_stream *stream, size_t num_transfers) +{ + struct bladerf_usb *usb = stream->dev->backend_data; + return usb->fn->init_stream(usb->driver, stream, num_transfers); +} + +static int usb_stream(struct bladerf_stream *stream, bladerf_channel_layout layout) +{ + struct bladerf_usb *usb = stream->dev->backend_data; + return usb->fn->stream(usb->driver, stream, layout); +} + +int usb_submit_stream_buffer(struct bladerf_stream *stream, void *buffer, + size_t *length, unsigned int timeout_ms, bool nonblock) +{ + struct bladerf_usb *usb = stream->dev->backend_data; + return usb->fn->submit_stream_buffer(usb->driver, stream, buffer, + length, timeout_ms, nonblock); +} + +static void usb_deinit_stream(struct bladerf_stream *stream) +{ + struct bladerf_usb *usb = stream->dev->backend_data; + usb->fn->deinit_stream(usb->driver, stream); +} + +/* + * Information about the boot image format and boot over USB can be found in + * Cypress AN76405: EZ-USB (R) FX3 (TM) Boot Options: + * http://www.cypress.com/?docID=49862 + * + * There's a request (bRequset = 0xc0) for the bootloader revision. + * However, there doesn't appear to be any documented reason to check this and + * behave differently depending upon the returned value. + */ + +/* Command fields for FX3 firmware upload vendor requests */ +#define FX3_BOOTLOADER_LOAD_BREQUEST 0xa0 +#define FX3_BOOTLOADER_ADDR_WVALUE(addr) (HOST_TO_LE16(addr & 0xffff)) +#define FX3_BOOTLOADER_ADDR_WINDEX(addr) (HOST_TO_LE16(((addr >> 16) & 0xffff))) +#define FX3_BOOTLOADER_MAX_LOAD_LEN 4096 + +static int write_and_verify_fw_chunk(struct bladerf_usb *usb, uint32_t addr, + uint8_t *data, uint32_t len, + uint8_t *readback_buf) { + + int status; + log_verbose("Writing %u bytes to bootloader @ 0x%08x\n", len, addr); + status = usb->fn->control_transfer(usb->driver, + USB_TARGET_DEVICE, + USB_REQUEST_VENDOR, + USB_DIR_HOST_TO_DEVICE, + FX3_BOOTLOADER_LOAD_BREQUEST, + FX3_BOOTLOADER_ADDR_WVALUE(addr), + FX3_BOOTLOADER_ADDR_WINDEX(addr), + data, + len, + CTRL_TIMEOUT_MS); + + if (status != 0) { + log_debug("Failed to write FW chunk (%d)\n", status); + return status; + } + + log_verbose("Reading back %u bytes from bootloader @ 0x%08x\n", len, addr); + status = usb->fn->control_transfer(usb->driver, + USB_TARGET_DEVICE, + USB_REQUEST_VENDOR, + USB_DIR_DEVICE_TO_HOST, + FX3_BOOTLOADER_LOAD_BREQUEST, + FX3_BOOTLOADER_ADDR_WVALUE(addr), + FX3_BOOTLOADER_ADDR_WINDEX(addr), + readback_buf, + len, + CTRL_TIMEOUT_MS); + + if (status != 0) { + log_debug("Failed to read back FW chunk (%d)\n", status); + return status; + } + + if (memcmp(data, readback_buf, len) != 0) { + log_debug("Readback did match written data.\n"); + status = BLADERF_ERR_UNEXPECTED; + } + + return status; +} + +static int execute_fw_from_bootloader(struct bladerf_usb *usb, uint32_t addr) +{ + int status; + + status = usb->fn->control_transfer(usb->driver, + USB_TARGET_DEVICE, + USB_REQUEST_VENDOR, + USB_DIR_HOST_TO_DEVICE, + FX3_BOOTLOADER_LOAD_BREQUEST, + FX3_BOOTLOADER_ADDR_WVALUE(addr), + FX3_BOOTLOADER_ADDR_WINDEX(addr), + NULL, + 0, + CTRL_TIMEOUT_MS); + + if (status != 0 && status != BLADERF_ERR_IO) { + log_debug("Failed to exec firmware: %s\n:", + bladerf_strerror(status)); + + } else if (status == BLADERF_ERR_IO) { + /* The device might drop out from underneath us as it starts executing + * the new firmware */ + log_verbose("Device returned IO error due to FW boot.\n"); + status = 0; + } else { + log_verbose("Booting new FW.\n"); + } + + return status; +} + +static int write_fw_to_bootloader(void *driver, struct fx3_firmware *fw) +{ + int status = 0; + uint32_t to_write; + uint32_t data_len; + uint32_t addr; + uint8_t *data; + bool got_section; + + uint8_t *readback = malloc(FX3_BOOTLOADER_MAX_LOAD_LEN); + if (readback == NULL) { + return BLADERF_ERR_MEM; + } + + do { + got_section = fx3_fw_next_section(fw, &addr, &data, &data_len); + if (got_section) { + /* data_len should never be zero, as fw->num_sections should NOT + * include the terminating section in its count */ + assert(data_len != 0); + + do { + to_write = u32_min(data_len, FX3_BOOTLOADER_MAX_LOAD_LEN); + + status = write_and_verify_fw_chunk(driver, + addr, data, to_write, + readback); + + data_len -= to_write; + addr += to_write; + data += to_write; + } while (data_len != 0 && status == 0); + } + } while (got_section && status == 0); + + if (status == 0) { + status = execute_fw_from_bootloader(driver, fx3_fw_entry_point(fw)); + } + + free(readback); + return status; +} + +static int usb_load_fw_from_bootloader(bladerf_backend backend, + uint8_t bus, uint8_t addr, + struct fx3_firmware *fw) +{ + int status = 0; + size_t i; + struct bladerf_usb usb; + + for (i = 0; i < ARRAY_SIZE(usb_driver_list); i++) { + + if ((backend == BLADERF_BACKEND_ANY) || + (usb_driver_list[i]->id == backend)) { + + usb.fn = usb_driver_list[i]->fn; + status = usb.fn->open_bootloader(&usb.driver, bus, addr); + if (status == 0) { + status = write_fw_to_bootloader(&usb, fw); + usb.fn->close_bootloader(usb.driver); + break; + } + } + } + + return status; +} + +/* Default handlers for operations unsupported by the NIOS II legacy packet + * format */ +static int set_vctcxo_tamer_mode_unsupported(struct bladerf *dev, + bladerf_vctcxo_tamer_mode mode) +{ + log_debug("Operation not supported with legacy NIOS packet format.\n"); + return BLADERF_ERR_UNSUPPORTED; +} + +static int get_vctcxo_tamer_mode_unsupported(struct bladerf *dev, + bladerf_vctcxo_tamer_mode *mode) +{ + *mode = BLADERF_VCTCXO_TAMER_INVALID; + log_debug("Operation not supported with legacy NIOS packet format.\n"); + return BLADERF_ERR_UNSUPPORTED; +} + +static int usb_read_fw_log(struct bladerf *dev, logger_entry *e) +{ + int status; + *e = LOG_EOF; + + status = vendor_cmd_int(dev, BLADE_USB_CMD_READ_LOG_ENTRY, + USB_DIR_DEVICE_TO_HOST, (int32_t*) e); + + return status; +} + +static int config_gpio_write(struct bladerf *dev, uint32_t val) +{ + struct bladerf_usb *usb = dev->backend_data; + bladerf_dev_speed usb_speed; + + if (usb->fn->get_speed(usb->driver, &usb_speed) != 0) { + log_debug("Error getting USB speed in %s\n", __FUNCTION__); + return BLADERF_ERR_UNEXPECTED; + } + + /* If we're connected at HS, we need to use smaller DMA transfers */ + if (usb_speed == BLADERF_DEVICE_SPEED_HIGH) { + val |= BLADERF_GPIO_FEATURE_SMALL_DMA_XFER; + } else if (usb_speed == BLADERF_DEVICE_SPEED_SUPER) { + val &= ~BLADERF_GPIO_FEATURE_SMALL_DMA_XFER; + } else { + assert(!"Encountered unknown USB speed"); + return BLADERF_ERR_UNEXPECTED; + } + + return nios_config_write(dev, val); +} + +static int set_agc_dc_correction_unsupported(struct bladerf *dev, + int16_t q_max, int16_t i_max, + int16_t q_mid, int16_t i_mid, + int16_t q_low, int16_t i_low) +{ + log_debug("Operation not supported with legacy NIOS packet format.\n"); + return BLADERF_ERR_UNSUPPORTED; +} + +static int legacy_config_gpio_write(struct bladerf *dev, uint32_t val) +{ + struct bladerf_usb *usb = dev->backend_data; + bladerf_dev_speed usb_speed; + + if (usb->fn->get_speed(usb->driver, &usb_speed) != 0) { + log_debug("Error getting USB speed in %s\n", __FUNCTION__); + return BLADERF_ERR_UNEXPECTED; + } + + /* If we're connected at HS, we need to use smaller DMA transfers */ + if (usb_speed == BLADERF_DEVICE_SPEED_HIGH) { + val |= BLADERF_GPIO_FEATURE_SMALL_DMA_XFER; + } else if (usb_speed == BLADERF_DEVICE_SPEED_SUPER) { + val &= ~BLADERF_GPIO_FEATURE_SMALL_DMA_XFER; + } else { + assert(!"Encountered unknown USB speed"); + return BLADERF_ERR_UNEXPECTED; + } + + return nios_legacy_config_write(dev, val); +} + +/* USB backend that used legacy format for communicating with NIOS II */ +const struct backend_fns backend_fns_usb_legacy = { + FIELD_INIT(.matches, usb_matches), + + FIELD_INIT(.probe, usb_probe), + + FIELD_INIT(.get_vid_pid, usb_get_vid_pid), + FIELD_INIT(.get_flash_id, usb_get_flash_id), + FIELD_INIT(.open, usb_open), + FIELD_INIT(.set_fpga_protocol, usb_set_fpga_protocol), + FIELD_INIT(.close, usb_close), + + FIELD_INIT(.is_fw_ready, usb_is_fw_ready), + + FIELD_INIT(.get_handle, usb_get_handle), + + FIELD_INIT(.load_fpga, usb_load_fpga), + FIELD_INIT(.is_fpga_configured, usb_is_fpga_configured), + FIELD_INIT(.get_fpga_source, usb_get_fpga_source), + + FIELD_INIT(.get_fw_version, usb_get_fw_version), + FIELD_INIT(.get_fpga_version, usb_get_fpga_version), + + FIELD_INIT(.erase_flash_blocks, usb_erase_flash_blocks), + FIELD_INIT(.read_flash_pages, usb_read_flash_pages), + FIELD_INIT(.write_flash_pages, usb_write_flash_pages), + + FIELD_INIT(.device_reset, usb_device_reset), + FIELD_INIT(.jump_to_bootloader, usb_jump_to_bootloader), + + FIELD_INIT(.get_cal, usb_get_cal), + FIELD_INIT(.get_otp, usb_get_otp), + FIELD_INIT(.write_otp, usb_write_otp), + FIELD_INIT(.lock_otp, usb_lock_otp), + FIELD_INIT(.get_device_speed, usb_get_device_speed), + + FIELD_INIT(.config_gpio_write, legacy_config_gpio_write), + FIELD_INIT(.config_gpio_read, nios_legacy_config_read), + + FIELD_INIT(.expansion_gpio_write, nios_legacy_expansion_gpio_write), + FIELD_INIT(.expansion_gpio_read, nios_legacy_expansion_gpio_read), + FIELD_INIT(.expansion_gpio_dir_write, nios_legacy_expansion_gpio_dir_write), + FIELD_INIT(.expansion_gpio_dir_read, nios_legacy_expansion_gpio_dir_read), + + FIELD_INIT(.set_iq_gain_correction, nios_legacy_set_iq_gain_correction), + FIELD_INIT(.set_iq_phase_correction, nios_legacy_set_iq_phase_correction), + FIELD_INIT(.get_iq_gain_correction, nios_legacy_get_iq_gain_correction), + FIELD_INIT(.get_iq_phase_correction, nios_legacy_get_iq_phase_correction), + + FIELD_INIT(.set_agc_dc_correction, set_agc_dc_correction_unsupported), + + FIELD_INIT(.get_timestamp, nios_legacy_get_timestamp), + + FIELD_INIT(.si5338_write, nios_legacy_si5338_write), + FIELD_INIT(.si5338_read, nios_legacy_si5338_read), + + FIELD_INIT(.lms_write, nios_legacy_lms6_write), + FIELD_INIT(.lms_read, nios_legacy_lms6_read), + + FIELD_INIT(.ina219_write, nios_legacy_ina219_write), + FIELD_INIT(.ina219_read, nios_legacy_ina219_read), + + FIELD_INIT(.ad9361_spi_write, nios_legacy_ad9361_spi_write), + FIELD_INIT(.ad9361_spi_read, nios_legacy_ad9361_spi_read), + + FIELD_INIT(.adi_axi_write, nios_legacy_adi_axi_write), + FIELD_INIT(.adi_axi_read, nios_legacy_adi_axi_read), + + FIELD_INIT(.rfic_command_write, nios_legacy_rfic_command_write), + FIELD_INIT(.rfic_command_read, nios_legacy_rfic_command_read), + + FIELD_INIT(.rffe_control_write, nios_legacy_rffe_control_write), + FIELD_INIT(.rffe_control_read, nios_legacy_rffe_control_read), + + FIELD_INIT(.rffe_fastlock_save, nios_legacy_rffe_fastlock_save), + + FIELD_INIT(.ad56x1_vctcxo_trim_dac_write, nios_legacy_ad56x1_vctcxo_trim_dac_write), + FIELD_INIT(.ad56x1_vctcxo_trim_dac_read, nios_legacy_ad56x1_vctcxo_trim_dac_read), + + FIELD_INIT(.adf400x_write, nios_legacy_adf400x_write), + FIELD_INIT(.adf400x_read, nios_legacy_adf400x_read), + + FIELD_INIT(.vctcxo_dac_write, nios_legacy_vctcxo_trim_dac_write), + FIELD_INIT(.vctcxo_dac_read, nios_vctcxo_trim_dac_read), + + FIELD_INIT(.set_vctcxo_tamer_mode, set_vctcxo_tamer_mode_unsupported), + FIELD_INIT(.get_vctcxo_tamer_mode, get_vctcxo_tamer_mode_unsupported), + + FIELD_INIT(.xb_spi, nios_legacy_xb200_synth_write), + + FIELD_INIT(.set_firmware_loopback, usb_set_firmware_loopback), + FIELD_INIT(.get_firmware_loopback, usb_get_firmware_loopback), + + FIELD_INIT(.enable_module, usb_enable_module), + + FIELD_INIT(.init_stream, usb_init_stream), + FIELD_INIT(.stream, usb_stream), + FIELD_INIT(.submit_stream_buffer, usb_submit_stream_buffer), + FIELD_INIT(.deinit_stream, usb_deinit_stream), + + FIELD_INIT(.retune, nios_retune), + FIELD_INIT(.retune2, nios_retune2), + + FIELD_INIT(.load_fw_from_bootloader, usb_load_fw_from_bootloader), + + FIELD_INIT(.read_fw_log, usb_read_fw_log), + + FIELD_INIT(.read_trigger, nios_legacy_read_trigger), + FIELD_INIT(.write_trigger, nios_legacy_write_trigger), + + FIELD_INIT(.name, "usb"), +}; + +/* USB backend for use with FPGA supporting update NIOS II packet formats */ +const struct backend_fns backend_fns_usb = { + FIELD_INIT(.matches, usb_matches), + + FIELD_INIT(.probe, usb_probe), + + FIELD_INIT(.get_vid_pid, usb_get_vid_pid), + FIELD_INIT(.get_flash_id, usb_get_flash_id), + FIELD_INIT(.open, usb_open), + FIELD_INIT(.set_fpga_protocol, usb_set_fpga_protocol), + FIELD_INIT(.close, usb_close), + + FIELD_INIT(.is_fw_ready, usb_is_fw_ready), + + FIELD_INIT(.get_handle, usb_get_handle), + + FIELD_INIT(.load_fpga, usb_load_fpga), + FIELD_INIT(.is_fpga_configured, usb_is_fpga_configured), + FIELD_INIT(.get_fpga_source, usb_get_fpga_source), + + FIELD_INIT(.get_fw_version, usb_get_fw_version), + FIELD_INIT(.get_fpga_version, usb_get_fpga_version), + + FIELD_INIT(.erase_flash_blocks, usb_erase_flash_blocks), + FIELD_INIT(.read_flash_pages, usb_read_flash_pages), + FIELD_INIT(.write_flash_pages, usb_write_flash_pages), + + FIELD_INIT(.device_reset, usb_device_reset), + FIELD_INIT(.jump_to_bootloader, usb_jump_to_bootloader), + + FIELD_INIT(.get_cal, usb_get_cal), + FIELD_INIT(.get_otp, usb_get_otp), + FIELD_INIT(.write_otp, usb_write_otp), + FIELD_INIT(.lock_otp, usb_lock_otp), + FIELD_INIT(.get_device_speed, usb_get_device_speed), + + FIELD_INIT(.config_gpio_write, config_gpio_write), + FIELD_INIT(.config_gpio_read, nios_config_read), + + FIELD_INIT(.expansion_gpio_write, nios_expansion_gpio_write), + FIELD_INIT(.expansion_gpio_read, nios_expansion_gpio_read), + FIELD_INIT(.expansion_gpio_dir_write, nios_expansion_gpio_dir_write), + FIELD_INIT(.expansion_gpio_dir_read, nios_expansion_gpio_dir_read), + + FIELD_INIT(.set_iq_gain_correction, nios_set_iq_gain_correction), + FIELD_INIT(.set_iq_phase_correction, nios_set_iq_phase_correction), + FIELD_INIT(.get_iq_gain_correction, nios_get_iq_gain_correction), + FIELD_INIT(.get_iq_phase_correction, nios_get_iq_phase_correction), + + FIELD_INIT(.set_agc_dc_correction, nios_set_agc_dc_correction), + + FIELD_INIT(.get_timestamp, nios_get_timestamp), + + FIELD_INIT(.si5338_write, nios_si5338_write), + FIELD_INIT(.si5338_read, nios_si5338_read), + + FIELD_INIT(.lms_write, nios_lms6_write), + FIELD_INIT(.lms_read, nios_lms6_read), + + FIELD_INIT(.ina219_write, nios_ina219_write), + FIELD_INIT(.ina219_read, nios_ina219_read), + + FIELD_INIT(.ad9361_spi_write, nios_ad9361_spi_write), + FIELD_INIT(.ad9361_spi_read, nios_ad9361_spi_read), + + FIELD_INIT(.adi_axi_write, nios_adi_axi_write), + FIELD_INIT(.adi_axi_read, nios_adi_axi_read), + + FIELD_INIT(.wishbone_master_write, nios_wishbone_master_write), + FIELD_INIT(.wishbone_master_read, nios_wishbone_master_read), + + FIELD_INIT(.rfic_command_write, nios_rfic_command_write), + FIELD_INIT(.rfic_command_read, nios_rfic_command_read), + + FIELD_INIT(.rffe_control_write, nios_rffe_control_write), + FIELD_INIT(.rffe_control_read, nios_rffe_control_read), + + FIELD_INIT(.rffe_fastlock_save, nios_rffe_fastlock_save), + + FIELD_INIT(.ad56x1_vctcxo_trim_dac_write, nios_ad56x1_vctcxo_trim_dac_write), + FIELD_INIT(.ad56x1_vctcxo_trim_dac_read, nios_ad56x1_vctcxo_trim_dac_read), + + FIELD_INIT(.adf400x_write, nios_adf400x_write), + FIELD_INIT(.adf400x_read, nios_adf400x_read), + + FIELD_INIT(.vctcxo_dac_write, nios_vctcxo_trim_dac_write), + FIELD_INIT(.vctcxo_dac_read, nios_vctcxo_trim_dac_read), + + FIELD_INIT(.set_vctcxo_tamer_mode, nios_set_vctcxo_tamer_mode), + FIELD_INIT(.get_vctcxo_tamer_mode, nios_get_vctcxo_tamer_mode), + + FIELD_INIT(.xb_spi, nios_xb200_synth_write), + + FIELD_INIT(.set_firmware_loopback, usb_set_firmware_loopback), + FIELD_INIT(.get_firmware_loopback, usb_get_firmware_loopback), + + FIELD_INIT(.enable_module, usb_enable_module), + + FIELD_INIT(.init_stream, usb_init_stream), + FIELD_INIT(.stream, usb_stream), + FIELD_INIT(.submit_stream_buffer, usb_submit_stream_buffer), + FIELD_INIT(.deinit_stream, usb_deinit_stream), + + FIELD_INIT(.retune, nios_retune), + FIELD_INIT(.retune2, nios_retune2), + + FIELD_INIT(.load_fw_from_bootloader, usb_load_fw_from_bootloader), + + FIELD_INIT(.read_fw_log, usb_read_fw_log), + + FIELD_INIT(.read_trigger, nios_read_trigger), + FIELD_INIT(.write_trigger, nios_write_trigger), + + FIELD_INIT(.name, "usb"), +}; diff --git a/Radio/HW/BladeRF/src/backend/usb/usb.h b/Radio/HW/BladeRF/src/backend/usb/usb.h new file mode 100644 index 0000000..644ed7a --- /dev/null +++ b/Radio/HW/BladeRF/src/backend/usb/usb.h @@ -0,0 +1,168 @@ +/* + * This file is part of the bladeRF project: + * http://www.github.com/nuand/bladeRF + * + * Copyright (C) 2014-2015 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 + */ + +#ifndef BACKEND_USB_H_ +#define BACKEND_USB_H_ + +#include "host_config.h" + +#include "board/board.h" + +#if ENABLE_USB_DEV_RESET_ON_OPEN +extern bool bladerf_usb_reset_device_on_open; +#endif + +#ifndef SAMPLE_EP_IN +#define SAMPLE_EP_IN 0x81 +#endif + +#ifndef SAMPLE_EP_OUT +#define SAMPLE_EP_OUT 0x01 +#endif + +#ifndef PERIPHERAL_EP_IN +#define PERIPHERAL_EP_IN 0x82 +#endif + +#ifndef PERIPHERAL_EP_OUT +#define PERIPHERAL_EP_OUT 0x02 +#endif + +#ifndef PERIPHERAL_TIMEOUT_MS +#define PERIPHERAL_TIMEOUT_MS 250 +#endif + +/* Be careful when lowering this value. The control request for flash erase + * operations take some time */ +#ifndef CTRL_TIMEOUT_MS +#define CTRL_TIMEOUT_MS 1000 +#endif + +#ifndef BULK_TIMEOUT_MS +#define BULK_TIMEOUT_MS 1000 +#endif + +/* Size of a host<->FPGA message in BYTES */ +#define USB_MSG_SIZE_SS 2048 +#define USB_MSG_SIZE_HS 1024 + +typedef enum { + USB_TARGET_DEVICE, + USB_TARGET_INTERFACE, + USB_TARGET_ENDPOINT, + USB_TARGET_OTHER +} usb_target; + +typedef enum { + USB_REQUEST_STANDARD, + USB_REQUEST_CLASS, + USB_REQUEST_VENDOR +} usb_request; + +typedef enum { + USB_DIR_HOST_TO_DEVICE = 0x00, + USB_DIR_DEVICE_TO_HOST = 0x80 +} usb_direction; + +/** + * USB backend driver function table + * + * All return values are expected to be 0 on success, or a BLADERF_ERR_* + * value on failure + */ +struct usb_fns { + int (*probe)(backend_probe_target probe_target, + struct bladerf_devinfo_list *info_list); + + /* Populates the `driver` pointer with a handle for the specific USB driver. + * `info_in` describes the device to open, and may contain wildcards. + * On success, the driver should fill in `info_out` with the complete + * details of the device. */ + int (*open)(void **driver, + struct bladerf_devinfo *info_in, + struct bladerf_devinfo *info_out); + + void (*close)(void *driver); + + int (*get_vid_pid)(void *driver, uint16_t *vid, uint16_t *pid); + + int (*get_flash_id)(void *driver, uint8_t *mid, uint8_t *did); + + int (*get_handle)(void *driver, void **handle); + + int (*get_speed)(void *driver, bladerf_dev_speed *speed); + + int (*change_setting)(void *driver, uint8_t setting); + + int (*control_transfer)(void *driver, + usb_target target_type, + usb_request req_type, + usb_direction direction, + uint8_t request, + uint16_t wvalue, + uint16_t windex, + void *buffer, + uint32_t buffer_len, + uint32_t timeout_ms); + + int (*bulk_transfer)(void *driver, + uint8_t endpoint, + void *buffer, + uint32_t buffer_len, + uint32_t timeout_ms); + + int (*get_string_descriptor)(void *driver, + uint8_t index, + void *buffer, + uint32_t buffer_len); + + int (*init_stream)(void *driver, + struct bladerf_stream *stream, + size_t num_transfers); + + int (*stream)(void *driver, + struct bladerf_stream *stream, + bladerf_channel_layout layout); + + int (*submit_stream_buffer)(void *driver, + struct bladerf_stream *stream, + void *buffer, + size_t *length, + unsigned int timeout_ms, + bool nonblock); + + int (*deinit_stream)(void *driver, struct bladerf_stream *stream); + + int (*open_bootloader)(void **driver, uint8_t bus, uint8_t addr); + void (*close_bootloader)(void *driver); +}; + +struct usb_driver { + const struct usb_fns *fn; + bladerf_backend id; +}; + +struct bladerf_usb { + const struct usb_fns *fn; + void *driver; +}; + +#endif -- cgit v1.2.3